home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 24 / AACD 24.iso / AACD / Utilities / ttf2pt1PPC / src / pt1.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-05-27  |  140.4 KB  |  5,967 lines

  1. /*
  2.  * see COPYRIGHT
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <fcntl.h>
  11. #include <time.h>
  12. #include <ctype.h>
  13. #include <math.h>
  14.  
  15. #ifndef WINDOWS
  16. #    include <netinet/in.h>
  17. #    include <unistd.h>
  18. #else
  19. #    include "windows.h"
  20. #endif
  21.  
  22. #include "ttf.h"
  23. #include "pt1.h"
  24. #include "global.h"
  25.  
  26. /* big and small values for comparisons */
  27. #define FBIGVAL    (1e20)
  28. #define FEPS    (100000./FBIGVAL)
  29.  
  30. int      stdhw, stdvw;    /* dominant stems widths */
  31. int      stemsnaph[12], stemsnapv[12];    /* most typical stem width */
  32.  
  33. int      bluevalues[14];
  34. int      nblues;
  35. int      otherblues[10];
  36. int      notherb;
  37. int      bbox[4];    /* the FontBBox array */
  38. double   italic_angle;
  39.  
  40. GLYPH   *glyph_list;
  41. int    encoding[ENCTABSZ];    /* inverse of glyph[].char_no */
  42. int    kerning_pairs = 0;
  43.  
  44. /* prototypes */
  45. static int isign( int x);
  46. static int fsign( double x);
  47. static void fixcvdir( GENTRY * ge, int dir);
  48. static void fixcvends( GENTRY * ge);
  49. static int fgetcvdir( GENTRY * ge);
  50. static int igetcvdir( GENTRY * ge);
  51. static int fiszigzag( GENTRY *ge);
  52. static int iiszigzag( GENTRY *ge);
  53. static GENTRY * freethisge( GENTRY *ge);
  54. static void addgeafter( GENTRY *oge, GENTRY *nge );
  55. static GENTRY * newgentry( int flags);
  56. static void debugstems( char *name, STEM * hstems, int nhs, STEM * vstems, int nvs);
  57. static int addbluestems( STEM *s, int n);
  58. static void sortstems( STEM * s, int n);
  59. static int stemoverlap( STEM * s1, STEM * s2);
  60. static int steminblue( STEM *s);
  61. static void markbluestems( STEM *s, int nold);
  62. static int joinmainstems( STEM * s, int nold, int useblues);
  63. static void joinsubstems( STEM * s, short *pairs, int nold, int useblues);
  64. static void fixendpath( GENTRY *ge);
  65. static void fdelsmall( GLYPH *g, double minlen);
  66. static double fcvarea( GENTRY *ge);
  67. static int fckjoinedcv( GLYPH *g, double t, GENTRY *nge, 
  68.     GENTRY *old1, GENTRY *old2, double k);
  69. static double fcvval( GENTRY *ge, int axis, double t);
  70. static double fclosegap( GENTRY *from, GENTRY *to, int axis,
  71.     double gap, double *ret);
  72.  
  73. static int
  74. isign(
  75.      int x
  76. )
  77. {
  78.     if (x > 0)
  79.         return 1;
  80.     else if (x < 0)
  81.         return -1;
  82.     else
  83.         return 0;
  84. }
  85.  
  86. static int
  87. fsign(
  88.      double x
  89. )
  90. {
  91.     if (x > 0.0)
  92.         return 1;
  93.     else if (x < 0.0)
  94.         return -1;
  95.     else
  96.         return 0;
  97. }
  98.  
  99. static GENTRY *
  100. newgentry(
  101.     int flags
  102. )
  103. {
  104.     GENTRY         *ge;
  105.  
  106.     ge = calloc(1, sizeof(GENTRY));
  107.  
  108.     if (ge == 0) {
  109.         fprintf(stderr, "***** Memory allocation error *****\n");
  110.         exit(255);
  111.     }
  112.     ge->stemid = -1;
  113.     ge->flags = flags;
  114.     /* the rest is set to 0 by calloc() */
  115.     return ge;
  116. }
  117.  
  118. /*
  119.  * Routines to print out Postscript functions with optimization
  120.  */
  121.  
  122. void
  123. rmoveto(
  124.     int dx,
  125.     int dy
  126. )
  127. {
  128.     if (optimize && dx == 0)
  129.         fprintf(pfa_file, "%d vmoveto\n", dy);
  130.     else if (optimize && dy == 0)
  131.         fprintf(pfa_file, "%d hmoveto\n", dx);
  132.     else
  133.         fprintf(pfa_file, "%d %d rmoveto\n", dx, dy);
  134. }
  135.  
  136. void
  137. rlineto(
  138.     int dx,
  139.     int dy
  140. )
  141. {
  142.     if (optimize && dx == 0 && dy == 0)    /* for special pathologic
  143.                          * case */
  144.         return;
  145.     else if (optimize && dx == 0)
  146.         fprintf(pfa_file, "%d vlineto\n", dy);
  147.     else if (optimize && dy == 0)
  148.         fprintf(pfa_file, "%d hlineto\n", dx);
  149.     else
  150.         fprintf(pfa_file, "%d %d rlineto\n", dx, dy);
  151. }
  152.  
  153. void
  154. rrcurveto(
  155.       int dx1,
  156.       int dy1,
  157.       int dx2,
  158.       int dy2,
  159.       int dx3,
  160.       int dy3
  161. )
  162. {
  163.     /* first two ifs are for crazy cases that occur surprisingly often */
  164.     if (optimize && dx1 == 0 && dx2 == 0 && dx3 == 0)
  165.         rlineto(0, dy1 + dy2 + dy3);
  166.     else if (optimize && dy1 == 0 && dy2 == 0 && dy3 == 0)
  167.         rlineto(dx1 + dx2 + dx3, 0);
  168.     else if (optimize && dy1 == 0 && dx3 == 0)
  169.         fprintf(pfa_file, "%d %d %d %d hvcurveto\n",
  170.             dx1, dx2, dy2, dy3);
  171.     else if (optimize && dx1 == 0 && dy3 == 0)
  172.         fprintf(pfa_file, "%d %d %d %d vhcurveto\n",
  173.             dy1, dx2, dy2, dx3);
  174.     else
  175.         fprintf(pfa_file, "%d %d %d %d %d %d rrcurveto\n",
  176.             dx1, dy1, dx2, dy2, dx3, dy3);
  177. }
  178.  
  179. void
  180. closepath(void)
  181. {
  182.     fprintf(pfa_file, "closepath\n");
  183. }
  184.  
  185. /*
  186.  * Many of the path processing routines exist (or will exist) in
  187.  * both floating-point and integer version. Fimally most of the
  188.  * processing will go in floating point and the integer processing
  189.  * will become legacy.
  190.  * The names of floating routines start with f, names of integer 
  191.  * routines start with i, and those old routines existing in one 
  192.  * version only have no such prefix at all.
  193.  */
  194.  
  195. /*
  196. ** Routine that checks integrity of the path, for debugging
  197. */
  198.  
  199. void
  200. assertpath(
  201.        GENTRY * from,
  202.        char *file,
  203.        int line,
  204.        char *name
  205. )
  206. {
  207.     GENTRY         *first, *pe, *ge;
  208.     int    isfloat;
  209.  
  210.     if(from==0)
  211.         return;
  212.     isfloat = (from->flags & GEF_FLOAT);
  213.     pe = from->prev;
  214.     for (ge = from; ge != 0; pe = ge, ge = ge->next) {
  215.         if( (ge->flags & GEF_FLOAT) ^ isfloat ) {
  216.             fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  217.             fprintf(stderr, "float flag changes from %s to %s at 0x%p (type %c, prev type %c)\n",
  218.                 (isfloat ? "TRUE" : "FALSE"), (isfloat ? "FALSE" : "TRUE"), ge, ge->type, pe->type);
  219.             abort();
  220.         }
  221.         if (pe != ge->prev) {
  222.             fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  223.             fprintf(stderr, "unidirectional chain 0x%x -next-> 0x%x -prev-> 0x%x \n",
  224.                 pe, ge, ge->prev);
  225.             abort();
  226.         }
  227.  
  228.         switch(ge->type) {
  229.         case GE_MOVE:
  230.             break;
  231.         case GE_PATH:
  232.             if (ge->prev == 0) {
  233.                 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  234.                 fprintf(stderr, "empty path at 0x%x \n", ge);
  235.                 abort();
  236.             }
  237.             break;
  238.         case GE_LINE:
  239.         case GE_CURVE:
  240.             if(ge->frwd->bkwd != ge) {
  241.                 fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  242.                 fprintf(stderr, "unidirectional chain 0x%x -frwd-> 0x%x -bkwd-> 0x%x \n",
  243.                     ge, ge->frwd, ge->frwd->bkwd);
  244.                 abort();
  245.             }
  246.             if(ge->prev->type == GE_MOVE) {
  247.                 first = ge;
  248.                 if(ge->bkwd->next->type != GE_PATH) {
  249.                     fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  250.                     fprintf(stderr, "broken first backlink 0x%x -bkwd-> 0x%x -next-> 0x%x \n",
  251.                         ge, ge->bkwd, ge->bkwd->next);
  252.                     abort();
  253.                 }
  254.             }
  255.             if(ge->next->type == GE_PATH) {
  256.                 if(ge->frwd != first) {
  257.                     fprintf(stderr, "**! assertpath: called from %s line %d (%s) ****\n", file, line, name);
  258.                     fprintf(stderr, "broken loop 0x%x -...-> 0x%x -frwd-> 0x%x \n",
  259.                         first, ge, ge->frwd);
  260.                     abort();
  261.                 }
  262.             }
  263.             break;
  264.         }
  265.  
  266.     }
  267. }
  268.  
  269. void
  270. assertisfloat(
  271.     GLYPH *g,
  272.     char *msg
  273. )
  274. {
  275.     if( !(g->flags & GF_FLOAT) ) {
  276.         fprintf(stderr, "**! Glyph %s is not float: %s\n", g->name, msg);
  277.         abort();
  278.     }
  279.     if(g->lastentry) {
  280.         if( !(g->lastentry->flags & GEF_FLOAT) ) {
  281.             fprintf(stderr, "**! Glyphs %s last entry is int: %s\n", g->name, msg);
  282.             abort();
  283.         }
  284.     }
  285. }
  286.  
  287. void
  288. assertisint(
  289.     GLYPH *g,
  290.     char *msg
  291. )
  292. {
  293.     if( (g->flags & GF_FLOAT) ) {
  294.         fprintf(stderr, "**! Glyph %s is not int: %s\n", g->name, msg);
  295.         abort();
  296.     }
  297.     if(g->lastentry) {
  298.         if( (g->lastentry->flags & GEF_FLOAT) ) {
  299.             fprintf(stderr, "**! Glyphs %s last entry is float: %s\n", g->name, msg);
  300.             abort();
  301.         }
  302.     }
  303. }
  304.  
  305.  
  306. /*
  307.  * Routines to save the generated data about glyph
  308.  */
  309.  
  310. void
  311. fg_rmoveto(
  312.       GLYPH * g,
  313.       double x,
  314.       double y)
  315. {
  316.     GENTRY         *oge;
  317.  
  318.     if (ISDBG(BUILDG))
  319.         fprintf(stderr, "%s: f rmoveto(%g, %g)\n", g->name, x, y);
  320.  
  321.     assertisfloat(g, "adding float MOVE");
  322.  
  323.     if ((oge = g->lastentry) != 0) {
  324.         if (oge->type == GE_MOVE) {    /* just eat up the first move */
  325.             oge->fx3 = x;
  326.             oge->fy3 = y;
  327.         } else if (oge->type == GE_LINE || oge->type == GE_CURVE) {
  328.             fprintf(stderr, "Glyph %s: MOVE in middle of path\n", g->name);
  329.         } else {
  330.             GENTRY         *nge;
  331.  
  332.             nge = newgentry(GEF_FLOAT);
  333.             nge->type = GE_MOVE;
  334.             nge->fx3 = x;
  335.             nge->fy3 = y;
  336.  
  337.             oge->next = nge;
  338.             nge->prev = oge;
  339.             g->lastentry = nge;
  340.         }
  341.     } else {
  342.         GENTRY         *nge;
  343.  
  344.         nge = newgentry(GEF_FLOAT);
  345.         nge->type = GE_MOVE;
  346.         nge->fx3 = x;
  347.         nge->fy3 = y;
  348.         nge->bkwd = (GENTRY*)&g->entries;
  349.         g->entries = g->lastentry = nge;
  350.     }
  351.  
  352.     if (0 && ISDBG(BUILDG))
  353.         dumppaths(g, NULL, NULL);
  354. }
  355.  
  356. void
  357. fg_rlineto(
  358.       GLYPH * g,
  359.       double x,
  360.       double y)
  361. {
  362.     GENTRY         *oge, *nge;
  363.  
  364.     if (ISDBG(BUILDG))
  365.         fprintf(stderr, "%s: f rlineto(%g, %g)\n", g->name, x, y);
  366.  
  367.     assertisfloat(g, "adding float LINE");
  368.  
  369.     nge = newgentry(GEF_FLOAT);
  370.     nge->type = GE_LINE;
  371.     nge->fx3 = x;
  372.     nge->fy3 = y;
  373.  
  374.     if ((oge = g->lastentry) != 0) {
  375.         if (x == oge->fx3 && y == oge->fy3) {    /* empty line */
  376.             /* ignore it or we will get in troubles later */
  377.             free(nge);
  378.             return;
  379.         }
  380.         if (g->path == 0) {
  381.             g->path = nge;
  382.             nge->bkwd = nge->frwd = nge;
  383.         } else {
  384.             oge->frwd = nge;
  385.             nge->bkwd = oge;
  386.             g->path->bkwd = nge;
  387.             nge->frwd = g->path;
  388.         }
  389.  
  390.         oge->next = nge;
  391.         nge->prev = oge;
  392.         g->lastentry = nge;
  393.     } else {
  394.         WARNING_1 fprintf(stderr, "Glyph %s: LINE outside of path\n", g->name);
  395.         free(nge);
  396.     }
  397.  
  398.     if (0 && ISDBG(BUILDG))
  399.         dumppaths(g, NULL, NULL);
  400. }
  401.  
  402. void
  403. fg_rrcurveto(
  404.         GLYPH * g,
  405.         double x1,
  406.         double y1,
  407.         double x2,
  408.         double y2,
  409.         double x3,
  410.         double y3)
  411. {
  412.     GENTRY         *oge, *nge;
  413.  
  414.     oge = g->lastentry;
  415.  
  416.     if (ISDBG(BUILDG))
  417.         fprintf(stderr, "%s: f rrcurveto(%g, %g, %g, %g, %g, %g)\n"
  418.             ,g->name, x1, y1, x2, y2, x3, y3);
  419.  
  420.     assertisfloat(g, "adding float CURVE");
  421.  
  422.     if (oge && oge->fx3 == x1 && x1 == x2 && x2 == x3)    /* check if it's
  423.                                  * actually a line */
  424.         fg_rlineto(g, x1, y3);
  425.     else if (oge && oge->fy3 == y1 && y1 == y2 && y2 == y3)
  426.         fg_rlineto(g, x3, y1);
  427.     else {
  428.         nge = newgentry(GEF_FLOAT);
  429.         nge->type = GE_CURVE;
  430.         nge->fx1 = x1;
  431.         nge->fy1 = y1;
  432.         nge->fx2 = x2;
  433.         nge->fy2 = y2;
  434.         nge->fx3 = x3;
  435.         nge->fy3 = y3;
  436.  
  437.         if (oge != 0) {
  438.             if (x3 == oge->fx3 && y3 == oge->fy3) {
  439.                 free(nge);    /* consider this curve empty */
  440.                 /* ignore it or we will get in troubles later */
  441.                 return;
  442.             }
  443.             if (g->path == 0) {
  444.                 g->path = nge;
  445.                 nge->bkwd = nge->frwd = nge;
  446.             } else {
  447.                 oge->frwd = nge;
  448.                 nge->bkwd = oge;
  449.                 g->path->bkwd = nge;
  450.                 nge->frwd = g->path;
  451.             }
  452.  
  453.             oge->next = nge;
  454.             nge->prev = oge;
  455.             g->lastentry = nge;
  456.         } else {
  457.             WARNING_1 fprintf(stderr, "Glyph %s: CURVE outside of path\n", g->name);
  458.             free(nge);
  459.         }
  460.     }
  461.  
  462.     if (0 && ISDBG(BUILDG))
  463.         dumppaths(g, NULL, NULL);
  464. }
  465.  
  466. void
  467. g_closepath(
  468.         GLYPH * g
  469. )
  470. {
  471.     GENTRY         *oge, *nge;
  472.  
  473.     if (ISDBG(BUILDG))
  474.         fprintf(stderr, "%s: closepath\n", g->name);
  475.  
  476.     oge = g->lastentry;
  477.  
  478.     if (g->path == 0) {
  479.         WARNING_1 fprintf(stderr, "Warning: **** closepath on empty path in glyph \"%s\" ****\n",
  480.             g->name);
  481.         if (oge == 0) {
  482.             WARNING_1 fprintf(stderr, "No previois entry\n");
  483.         } else {
  484.             WARNING_1 fprintf(stderr, "Previous entry type: %c\n", oge->type);
  485.             if (oge->type == GE_MOVE) {
  486.                 g->lastentry = oge->prev;
  487.                 if (oge->prev == 0)
  488.                     g->entries = 0;
  489.             }
  490.         }
  491.         return;
  492.     }
  493.  
  494.     nge = newgentry(oge->flags & GEF_FLOAT); /* keep the same type */
  495.     nge->type = GE_PATH;
  496.  
  497.     g->path = 0;
  498.  
  499.     oge->next = nge;
  500.     nge->prev = oge;
  501.     g->lastentry = nge;
  502.  
  503.     if (0 && ISDBG(BUILDG))
  504.         dumppaths(g, NULL, NULL);
  505. }
  506.  
  507. /*
  508.  * * SB * Routines to smooth and fix the glyphs
  509.  */
  510.  
  511. /*
  512. ** we don't want to see the curves with coinciding middle and
  513. ** outer points
  514. */
  515.  
  516. static void
  517. fixcvends(
  518.       GENTRY * ge
  519. )
  520. {
  521.     int             dx, dy;
  522.     int             x0, y0, x1, y1, x2, y2, x3, y3;
  523.  
  524.     if (ge->type != GE_CURVE)
  525.         return;
  526.  
  527.     if(ge->flags & GEF_FLOAT) {
  528.         fprintf(stderr, "**! fixcvends(0x%x) on floating entry, ABORT\n", ge);
  529.         abort(); /* dump core */
  530.     }
  531.  
  532.     x0 = ge->prev->ix3;
  533.     y0 = ge->prev->iy3;
  534.     x1 = ge->ix1;
  535.     y1 = ge->iy1;
  536.     x2 = ge->ix2;
  537.     y2 = ge->iy2;
  538.     x3 = ge->ix3;
  539.     y3 = ge->iy3;
  540.  
  541.  
  542.     /* look at the start of the curve */
  543.     if (x1 == x0 && y1 == y0) {
  544.         dx = x2 - x1;
  545.         dy = y2 - y1;
  546.  
  547.         if (dx == 0 && dy == 0
  548.             || x2 == x3 && y2 == y3) {
  549.             /* Oops, we actually have a straight line */
  550.             /*
  551.              * if it's small, we hope that it will get optimized
  552.              * later
  553.              */
  554.             if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) {
  555.                 ge->ix1 = x3;
  556.                 ge->iy1 = y3;
  557.                 ge->ix2 = x0;
  558.                 ge->iy2 = y0;
  559.             } else {/* just make it a line */
  560.                 ge->type = GE_LINE;
  561.             }
  562.         } else {
  563.             if (abs(dx) < 4 && abs(dy) < 4) {    /* consider it very
  564.                                  * small */
  565.                 ge->ix1 = x2;
  566.                 ge->iy1 = y2;
  567.             } else if (abs(dx) < 8 && abs(dy) < 8) {    /* consider it small */
  568.                 ge->ix1 += dx / 2;
  569.                 ge->iy1 += dy / 2;
  570.             } else {
  571.                 ge->ix1 += dx / 4;
  572.                 ge->iy1 += dy / 4;
  573.             }
  574.             /* make sure that it's still on the same side */
  575.             if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) {
  576.                 if (abs(x3 - x0) * abs(ge->iy1 - y0) > abs(y3 - y0) * abs(ge->ix1 - x0))
  577.                     ge->ix1 += isign(dx);
  578.             } else {
  579.                 if (abs(x3 - x0) * abs(ge->iy1 - y0) < abs(y3 - y0) * abs(ge->ix1 - x0))
  580.                     ge->iy1 += isign(dy);
  581.             }
  582.  
  583.             ge->ix2 += (x3 - x2) / 8;
  584.             ge->iy2 += (y3 - y2) / 8;
  585.             /* make sure that it's still on the same side */
  586.             if (abs(x3 - x0) * abs(y3 - y2) < abs(y3 - y0) * abs(x3 - x2)) {
  587.                 if (abs(x3 - x0) * abs(y3 - ge->iy2) > abs(y3 - y0) * abs(x3 - ge->ix2))
  588.                     ge->iy1 -= isign(y3 - y2);
  589.             } else {
  590.                 if (abs(x3 - x0) * abs(y3 - ge->iy2) < abs(y3 - y0) * abs(x3 - ge->ix2))
  591.                     ge->ix1 -= isign(x3 - x2);
  592.             }
  593.  
  594.         }
  595.     } else if (x2 == x3 && y2 == y3) {
  596.         dx = x1 - x2;
  597.         dy = y1 - y2;
  598.  
  599.         if (dx == 0 && dy == 0) {
  600.             /* Oops, we actually have a straight line */
  601.             /*
  602.              * if it's small, we hope that it will get optimized
  603.              * later
  604.              */
  605.             if (abs(x3 - x0) <= 2 || abs(y3 - y0) <= 2) {
  606.                 ge->ix1 = x3;
  607.                 ge->iy1 = y3;
  608.                 ge->ix2 = x0;
  609.                 ge->iy2 = y0;
  610.             } else {/* just make it a line */
  611.                 ge->type = GE_LINE;
  612.             }
  613.         } else {
  614.             if (abs(dx) < 4 && abs(dy) < 4) {    /* consider it very
  615.                                  * small */
  616.                 ge->ix2 = x1;
  617.                 ge->iy2 = y1;
  618.             } else if (abs(dx) < 8 && abs(dy) < 8) {    /* consider it small */
  619.                 ge->ix2 += dx / 2;
  620.                 ge->iy2 += dy / 2;
  621.             } else {
  622.                 ge->ix2 += dx / 4;
  623.                 ge->iy2 += dy / 4;
  624.             }
  625.             /* make sure that it's still on the same side */
  626.             if (abs(x3 - x0) * abs(dy) < abs(y3 - y0) * abs(dx)) {
  627.                 if (abs(x3 - x0) * abs(ge->iy2 - y3) > abs(y3 - y0) * abs(ge->ix2 - x3))
  628.                     ge->ix2 += isign(dx);
  629.             } else {
  630.                 if (abs(x3 - x0) * abs(ge->iy2 - y3) < abs(y3 - y0) * abs(ge->ix2 - x3))
  631.                     ge->iy2 += isign(dy);
  632.             }
  633.  
  634.             ge->ix1 += (x0 - x1) / 8;
  635.             ge->iy1 += (y0 - y1) / 8;
  636.             /* make sure that it's still on the same side */
  637.             if (abs(x3 - x0) * abs(y0 - y1) < abs(y3 - y0) * abs(x0 - x1)) {
  638.                 if (abs(x3 - x0) * abs(y0 - ge->iy1) > abs(y3 - y0) * abs(x0 - ge->ix1))
  639.                     ge->iy1 -= isign(y0 - y1);
  640.             } else {
  641.                 if (abs(x3 - x0) * abs(y0 - ge->iy1) < abs(y3 - y0) * abs(x0 - ge->ix1))
  642.                     ge->ix1 -= isign(x0 - x1);
  643.             }
  644.  
  645.         }
  646.     }
  647. }
  648.  
  649. /* if we have any curves that are in fact flat but
  650. ** are not horizontal nor vertical, substitute
  651. ** them also with lines
  652. */
  653.  
  654. void
  655. flattencurves(
  656.           GLYPH * g
  657. )
  658. {
  659.     GENTRY         *ge;
  660.     int             x0, y0, x1, y1, x2, y2, x3, y3;
  661.  
  662.     assertisint(g, "flattencurves INT");
  663.  
  664.     for (ge = g->entries; ge != 0; ge = ge->next) {
  665.         if (ge->type != GE_CURVE)
  666.             continue;
  667.  
  668.         x0 = ge->prev->ix3;
  669.         y0 = ge->prev->iy3;
  670.         x1 = ge->ix1;
  671.         y1 = ge->iy1;
  672.         x2 = ge->ix2;
  673.         y2 = ge->iy2;
  674.         x3 = ge->ix3;
  675.         y3 = ge->iy3;
  676.  
  677.         if ((x1 - x0) * (y2 - y1) == (x2 - x1) * (y1 - y0)
  678.             && (x1 - x0) * (y3 - y2) == (x3 - x2) * (y1 - y0)) {
  679.             ge->type = GE_LINE;
  680.         }
  681.     }
  682. }
  683.  
  684. /*
  685. ** After transformations we want to make sure that the resulting
  686. ** curve is going in the same quadrant as the original one,
  687. ** because rounding errors introduced during transformations
  688. ** may make the result completeley wrong.
  689. **
  690. ** `dir' argument describes the direction of the original curve,
  691. ** it is the superposition of two values for the front and
  692. ** rear ends of curve:
  693. **
  694. ** >EQUAL - goes over the line connecting the ends
  695. ** =EQUAL - coincides with the line connecting the ends
  696. ** <EQUAL - goes under the line connecting the ends
  697. **
  698. ** See CVDIR_* for exact definitions.
  699. */
  700.  
  701. static void
  702. fixcvdir(
  703.      GENTRY * ge,
  704.      int dir
  705. )
  706. {
  707.     int             a, b, c, d;
  708.     double          kk, kk1, kk2;
  709.     int             changed;
  710.     int             fdir, rdir;
  711.  
  712.     if(ge->flags & GEF_FLOAT) {
  713.         fprintf(stderr, "**! fixcvdir(0x%x) on floating entry, ABORT\n", ge);
  714.         abort(); /* dump core */
  715.     }
  716.  
  717.     fdir = (dir & CVDIR_FRONT) - CVDIR_FEQUAL;
  718.     if ((dir & CVDIR_REAR) == CVDIR_RSAME)
  719.         rdir = fdir; /* we need only isign, exact value doesn't matter */
  720.     else
  721.         rdir = (dir & CVDIR_REAR) - CVDIR_REQUAL;
  722.  
  723.     fixcvends(ge);
  724.  
  725.     c = isign(ge->ix3 - ge->prev->ix3);    /* note the direction of
  726.                          * curve */
  727.     d = isign(ge->iy3 - ge->prev->iy3);
  728.  
  729.     a = ge->iy3 - ge->prev->iy3;
  730.     b = ge->ix3 - ge->prev->ix3;
  731.     kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  732.     a = ge->iy1 - ge->prev->iy3;
  733.     b = ge->ix1 - ge->prev->ix3;
  734.     kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  735.     a = ge->iy3 - ge->iy2;
  736.     b = ge->ix3 - ge->ix2;
  737.     kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  738.  
  739.     changed = 1;
  740.     while (changed) {
  741.         if (ISDBG(FIXCVDIR)) {
  742.             /* for debugging */
  743.             fprintf(stderr, "fixcvdir %d %d (%d %d %d %d %d %d) %f %f %f\n",
  744.                 fdir, rdir,
  745.                 ge->ix1 - ge->prev->ix3,
  746.                 ge->iy1 - ge->prev->iy3,
  747.                 ge->ix2 - ge->ix1,
  748.                 ge->iy2 - ge->iy1,
  749.                 ge->ix3 - ge->ix2,
  750.                 ge->iy3 - ge->iy2,
  751.                 kk1, kk, kk2);
  752.         }
  753.         changed = 0;
  754.  
  755.         if (fdir > 0) {
  756.             if (kk1 > kk) {    /* the front end has problems */
  757.                 if (c * (ge->ix1 - ge->prev->ix3) > 0) {
  758.                     ge->ix1 -= c;
  759.                     changed = 1;
  760.                 } if (d * (ge->iy2 - ge->iy1) > 0) {
  761.                     ge->iy1 += d;
  762.                     changed = 1;
  763.                 }
  764.                 /* recalculate the coefficients */
  765.                 a = ge->iy3 - ge->prev->iy3;
  766.                 b = ge->ix3 - ge->prev->ix3;
  767.                 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  768.                 a = ge->iy1 - ge->prev->iy3;
  769.                 b = ge->ix1 - ge->prev->ix3;
  770.                 kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  771.             }
  772.         } else if (fdir < 0) {
  773.             if (kk1 < kk) {    /* the front end has problems */
  774.                 if (c * (ge->ix2 - ge->ix1) > 0) {
  775.                     ge->ix1 += c;
  776.                     changed = 1;
  777.                 } if (d * (ge->iy1 - ge->prev->iy3) > 0) {
  778.                     ge->iy1 -= d;
  779.                     changed = 1;
  780.                 }
  781.                 /* recalculate the coefficients */
  782.                 a = ge->iy1 - ge->prev->iy3;
  783.                 b = ge->ix1 - ge->prev->ix3;
  784.                 kk1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  785.                 a = ge->iy3 - ge->prev->iy3;
  786.                 b = ge->ix3 - ge->prev->ix3;
  787.                 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  788.             }
  789.         }
  790.         if (rdir > 0) {
  791.             if (kk2 < kk) {    /* the rear end has problems */
  792.                 if (c * (ge->ix2 - ge->ix1) > 0) {
  793.                     ge->ix2 -= c;
  794.                     changed = 1;
  795.                 } if (d * (ge->iy3 - ge->iy2) > 0) {
  796.                     ge->iy2 += d;
  797.                     changed = 1;
  798.                 }
  799.                 /* recalculate the coefficients */
  800.                 a = ge->iy3 - ge->prev->iy3;
  801.                 b = ge->ix3 - ge->prev->ix3;
  802.                 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  803.                 a = ge->iy3 - ge->iy2;
  804.                 b = ge->ix3 - ge->ix2;
  805.                 kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  806.             }
  807.         } else if (rdir < 0) {
  808.             if (kk2 > kk) {    /* the rear end has problems */
  809.                 if (c * (ge->ix3 - ge->ix2) > 0) {
  810.                     ge->ix2 += c;
  811.                     changed = 1;
  812.                 } if (d * (ge->iy2 - ge->iy1) > 0) {
  813.                     ge->iy2 -= d;
  814.                     changed = 1;
  815.                 }
  816.                 /* recalculate the coefficients */
  817.                 a = ge->iy3 - ge->prev->iy3;
  818.                 b = ge->ix3 - ge->prev->ix3;
  819.                 kk = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  820.                 a = ge->iy3 - ge->iy2;
  821.                 b = ge->ix3 - ge->ix2;
  822.                 kk2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  823.             }
  824.         }
  825.     }
  826.     fixcvends(ge);
  827. }
  828.  
  829. /* Get the directions of ends of curve for further usage */
  830.  
  831. /* expects that the previous element is also float */
  832.  
  833. static int
  834. fgetcvdir(
  835.      GENTRY * ge
  836. )
  837. {
  838.     double          a, b;
  839.     double          k, k1, k2;
  840.     int             dir = 0;
  841.  
  842.     if( !(ge->flags & GEF_FLOAT) ) {
  843.         fprintf(stderr, "**! fgetcvdir(0x%x) on int entry, ABORT\n", ge);
  844.         abort(); /* dump core */
  845.     }
  846.  
  847.     a = ge->fy3 - ge->prev->fy3;
  848.     b = ge->fx3 - ge->prev->fx3;
  849.     k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a));
  850.     a = ge->fy1 - ge->prev->fy3;
  851.     b = ge->fx1 - ge->prev->fx3;
  852.     k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a));
  853.     a = ge->fy3 - ge->fy2;
  854.     b = ge->fx3 - ge->fx2;
  855.     k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ( b / a));
  856.  
  857.     if (k1 < k)
  858.         dir |= CVDIR_FUP;
  859.     else if (k1 > k)
  860.         dir |= CVDIR_FDOWN;
  861.     else
  862.         dir |= CVDIR_FEQUAL;
  863.  
  864.     if (k2 > k)
  865.         dir |= CVDIR_RUP;
  866.     else if (k2 < k)
  867.         dir |= CVDIR_RDOWN;
  868.     else
  869.         dir |= CVDIR_REQUAL;
  870.  
  871.     return dir;
  872. }
  873.  
  874.  
  875. /* expects that the previous element is also int */
  876.  
  877. static int
  878. igetcvdir(
  879.      GENTRY * ge
  880. )
  881. {
  882.     int             a, b;
  883.     double          k, k1, k2;
  884.     int             dir = 0;
  885.  
  886.     if(ge->flags & GEF_FLOAT) {
  887.         fprintf(stderr, "**! igetcvdir(0x%x) on floating entry, ABORT\n", ge);
  888.         abort(); /* dump core */
  889.     }
  890.  
  891.     a = ge->iy3 - ge->prev->iy3;
  892.     b = ge->ix3 - ge->prev->ix3;
  893.     k = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  894.     a = ge->iy1 - ge->prev->iy3;
  895.     b = ge->ix1 - ge->prev->ix3;
  896.     k1 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  897.     a = ge->iy3 - ge->iy2;
  898.     b = ge->ix3 - ge->ix2;
  899.     k2 = fabs(a == 0 ? (b == 0 ? 1. : 100000.) : ((double) b / (double) a));
  900.  
  901.     if (k1 < k)
  902.         dir |= CVDIR_FUP;
  903.     else if (k1 > k)
  904.         dir |= CVDIR_FDOWN;
  905.     else
  906.         dir |= CVDIR_FEQUAL;
  907.  
  908.     if (k2 > k)
  909.         dir |= CVDIR_RUP;
  910.     else if (k2 < k)
  911.         dir |= CVDIR_RDOWN;
  912.     else
  913.         dir |= CVDIR_REQUAL;
  914.  
  915.     return dir;
  916. }
  917.  
  918. #if 0
  919. /* a function just to test the work of fixcvdir() */
  920. static void
  921. testfixcvdir(
  922.          GLYPH * g
  923. )
  924. {
  925.     GENTRY         *ge;
  926.     int             dir;
  927.  
  928.     for (ge = g->entries; ge != 0; ge = ge->next) {
  929.         if (ge->type == GE_CURVE) {
  930.             dir = igetcvdir(ge);
  931.             fixcvdir(ge, dir);
  932.         }
  933.     }
  934. }
  935. #endif
  936.  
  937. static int
  938. iround(
  939.     double val
  940. )
  941. {
  942.     return (int) (val > 0 ? val + 0.5 : val - 0.5);
  943. }
  944.     
  945. /* for debugging - dump the glyph
  946.  * mark with a star the entries from start to end inclusive
  947.  * (start == NULL means don't mark any, end == NULL means to the last)
  948.  */
  949.  
  950. void
  951. dumppaths(
  952.     GLYPH *g,
  953.     GENTRY *start,
  954.     GENTRY *end
  955. )
  956. {
  957.     GENTRY *ge;
  958.     int i;
  959.     char mark=' ';
  960.  
  961.     fprintf(stderr, "Glyph %s:\n", g->name);
  962.  
  963.     /* now do the conversion */
  964.     for(ge = g->entries; ge != 0; ge = ge->next) {
  965.         if(ge == start)
  966.             mark = '*';
  967.         fprintf(stderr, " %c %8x", mark, ge);
  968.         switch(ge->type) {
  969.         case GE_MOVE:
  970.         case GE_LINE:
  971.             if(ge->flags & GEF_FLOAT)
  972.                 fprintf(stderr," %c float (%g, %g)\n", ge->type, ge->fx3, ge->fy3);
  973.             else
  974.                 fprintf(stderr," %c int (%d, %d)\n", ge->type, ge->ix3, ge->iy3);
  975.             break;
  976.         case GE_CURVE:
  977.             if(ge->flags & GEF_FLOAT) {
  978.                 fprintf(stderr," C float ");
  979.                 for(i=0; i<3; i++)
  980.                     fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]);
  981.                 fprintf(stderr,"\n");
  982.             } else {
  983.                 fprintf(stderr," C int ");
  984.                 for(i=0; i<3; i++)
  985.                     fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
  986.                 fprintf(stderr,"\n");
  987.             }
  988.             break;
  989.         default:
  990.             fprintf(stderr, " %c\n", ge->type);
  991.             break;
  992.         }
  993.         if(ge == end)
  994.             mark = ' ';
  995.     }
  996. }
  997.  
  998. /*
  999.  * Routine that converts all entries in the path from float to int
  1000.  */
  1001.  
  1002. void
  1003. pathtoint(
  1004.     GLYPH *g
  1005. )
  1006. {
  1007.     GENTRY *ge;
  1008.     int x[3], y[3];
  1009.     int i;
  1010.  
  1011.  
  1012.     if(ISDBG(TOINT))
  1013.         fprintf(stderr, "TOINT: glyph %s\n", g->name);
  1014.     assertisfloat(g, "converting path to int\n");
  1015.  
  1016.     fdelsmall(g, 1.0); /* get rid of sub-pixel contours */
  1017.     assertpath(g->entries, __FILE__, __LINE__, g->name);
  1018.  
  1019.     /* 1st pass, collect the directions of the curves: have
  1020.      * to do that in advance, while everyting is float
  1021.      */
  1022.     for(ge = g->entries; ge != 0; ge = ge->next) {
  1023.         if( !(ge->flags & GEF_FLOAT) ) {
  1024.             fprintf(stderr, "**! glyphs %s has int entry, found in conversion to int\n",
  1025.                 g->name);
  1026.             exit(1);
  1027.         }
  1028.         if(ge->type == GE_CURVE) {
  1029.             ge->dir = fgetcvdir(ge);
  1030.         }
  1031.     }
  1032.  
  1033.     /* now do the conversion */
  1034.     for(ge = g->entries; ge != 0; ge = ge->next) {
  1035.         switch(ge->type) {
  1036.         case GE_MOVE:
  1037.         case GE_LINE:
  1038.             if(ISDBG(TOINT))
  1039.                 fprintf(stderr," %c float x=%g y=%g\n", ge->type, ge->fx3, ge->fy3);
  1040.             x[0] = iround(ge->fx3);
  1041.             y[0] = iround(ge->fy3);
  1042.             for(i=0; i<3; i++) { /* put some valid values everywhere, for convenience */
  1043.                 ge->ixn[i] = x[0];
  1044.                 ge->iyn[i] = y[0];
  1045.             }
  1046.             if(ISDBG(TOINT))
  1047.                 fprintf(stderr,"   int   x=%d y=%d\n", ge->ix3, ge->iy3);
  1048.             break;
  1049.         case GE_CURVE:
  1050.             if(ISDBG(TOINT))
  1051.                 fprintf(stderr," %c float ", ge->type);
  1052.  
  1053.             for(i=0; i<3; i++) {
  1054.                 if(ISDBG(TOINT))
  1055.                     fprintf(stderr,"(%g, %g) ", ge->fxn[i], ge->fyn[i]);
  1056.                 x[i] = iround(ge->fxn[i]);
  1057.                 y[i] = iround(ge->fyn[i]);
  1058.             }
  1059.  
  1060.             if(ISDBG(TOINT))
  1061.                 fprintf(stderr,"\n   int   ");
  1062.  
  1063.             for(i=0; i<3; i++) {
  1064.                 ge->ixn[i] = x[i];
  1065.                 ge->iyn[i] = y[i];
  1066.                 if(ISDBG(TOINT))
  1067.                     fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
  1068.             }
  1069.             ge->flags &= ~GEF_FLOAT; /* for fixcvdir */
  1070.             fixcvdir(ge, ge->dir);
  1071.  
  1072.             if(ISDBG(TOINT)) {
  1073.                 fprintf(stderr,"\n   fixed ");
  1074.                 for(i=0; i<3; i++)
  1075.                     fprintf(stderr,"(%d, %d) ", ge->ixn[i], ge->iyn[i]);
  1076.                 fprintf(stderr,"\n");
  1077.             }
  1078.  
  1079.             break;
  1080.         }
  1081.         ge->flags &= ~GEF_FLOAT;
  1082.     }
  1083.     g->flags &= ~GF_FLOAT;
  1084. }
  1085.  
  1086.  
  1087. /* check whether we can fix up the curve to change its size by (dx,dy) */
  1088. /* 0 means NO, 1 means YES */
  1089.  
  1090. /* for float: if scaling would be under 10% */
  1091.  
  1092. int
  1093. fcheckcv(
  1094.     GENTRY * ge,
  1095.     double dx,
  1096.     double dy
  1097. )
  1098. {
  1099.     if( !(ge->flags & GEF_FLOAT) ) {
  1100.         fprintf(stderr, "**! fcheckcv(0x%x) on int entry, ABORT\n", ge);
  1101.         abort(); /* dump core */
  1102.     }
  1103.  
  1104.     if (ge->type != GE_CURVE)
  1105.         return 0;
  1106.  
  1107.     if( fabs(ge->fx3 - ge->prev->fx3) < fabs(dx) * 10 )
  1108.         return 0;
  1109.  
  1110.     if( fabs(ge->fy3 - ge->prev->fy3) < fabs(dy) * 10 )
  1111.         return 0;
  1112.  
  1113.     return 1;
  1114. }
  1115.  
  1116. /* for int: if won't create new zigzags at the ends */
  1117.  
  1118. int
  1119. icheckcv(
  1120.     GENTRY * ge,
  1121.     int dx,
  1122.     int dy
  1123. )
  1124. {
  1125.     int             xdep, ydep;
  1126.  
  1127.     if(ge->flags & GEF_FLOAT) {
  1128.         fprintf(stderr, "**! icheckcv(0x%x) on floating entry, ABORT\n", ge);
  1129.         abort(); /* dump core */
  1130.     }
  1131.  
  1132.     if (ge->type != GE_CURVE)
  1133.         return 0;
  1134.  
  1135.     xdep = ge->ix3 - ge->prev->ix3;
  1136.     ydep = ge->iy3 - ge->prev->iy3;
  1137.  
  1138.     if (ge->type == GE_CURVE
  1139.         && (xdep * (xdep + dx)) > 0
  1140.         && (ydep * (ydep + dy)) > 0) {
  1141.         return 1;
  1142.     } else
  1143.         return 0;
  1144. }
  1145.  
  1146. /* float connect the ends of open contours */
  1147.  
  1148. void
  1149. fclosepaths(
  1150.        GLYPH * g
  1151. )
  1152. {
  1153.     GENTRY         *ge, *fge, *xge, *nge;
  1154.     int             i;
  1155.  
  1156.     assertisfloat(g, "fclosepaths float\n");
  1157.  
  1158.     for (xge = g->entries; xge != 0; xge = xge->next) {
  1159.         if( xge->type != GE_PATH )
  1160.             continue;
  1161.  
  1162.         ge = xge->prev;
  1163.         if(ge == 0 || ge->type != GE_LINE && ge->type!= GE_CURVE) {
  1164.             fprintf(stderr, "**! Glyph %s got empty path\n",
  1165.                 g->name);
  1166.             exit(1);
  1167.         }
  1168.  
  1169.         fge = ge->frwd;
  1170.         if (fge->prev == 0 || fge->prev->type != GE_MOVE) {
  1171.             fprintf(stderr, "**! Glyph %s got strange beginning of path\n",
  1172.                 g->name);
  1173.             exit(1);
  1174.         }
  1175.         fge = fge->prev;
  1176.         if (fge->fx3 != ge->fx3 || fge->fy3 != ge->fy3) {
  1177.             /* we have to fix this open path */
  1178.  
  1179.             WARNING_4 fprintf(stderr, "Glyph %s got path open by dx=%g dy=%g\n",
  1180.             g->name, fge->fx3 - ge->fx3, fge->fy3 - ge->fy3);
  1181.  
  1182.  
  1183.             /* add a new line */
  1184.             nge = newgentry(GEF_FLOAT);
  1185.             (*nge) = (*ge);
  1186.             nge->fx3 = fge->fx3;
  1187.             nge->fy3 = fge->fy3;
  1188.             nge->type = GE_LINE;
  1189.  
  1190.             addgeafter(ge, nge);
  1191.  
  1192.             if (fabs(ge->fx3 - fge->fx3) <= 2 && fabs(ge->fy3 - fge->fy3) <= 2) {
  1193.                 /*
  1194.                  * small change, try to get rid of the new entry
  1195.                  */
  1196.  
  1197.                 double df[2];
  1198.  
  1199.                 for(i=0; i<2; i++) {
  1200.                     df[i] = ge->fpoints[i][2] - fge->fpoints[i][2];
  1201.                     df[i] = fclosegap(nge, nge, i, df[i], NULL);
  1202.                 }
  1203.  
  1204.                 if(df[0] == 0. && df[1] == 0.) {
  1205.                     /* closed gap successfully, remove the added entry */
  1206.                     freethisge(nge);
  1207.                 }
  1208.             }
  1209.         }
  1210.     }
  1211. }
  1212.  
  1213. void
  1214. smoothjoints(
  1215.          GLYPH * g
  1216. )
  1217. {
  1218.     GENTRY         *ge, *ne;
  1219.     int             dx1, dy1, dx2, dy2, k;
  1220.     int             dir;
  1221.  
  1222.     return; /* this stuff seems to create problems */
  1223.  
  1224.     assertisint(g, "smoothjoints int");
  1225.  
  1226.     if (g->entries == 0)    /* nothing to do */
  1227.         return;
  1228.  
  1229.     for (ge = g->entries->next; ge != 0; ge = ge->next) {
  1230.         ne = ge->frwd;
  1231.  
  1232.         /*
  1233.          * although there should be no one-line path * and any path
  1234.          * must end with CLOSEPATH, * nobody can say for sure
  1235.          */
  1236.  
  1237.         if (ge == ne || ne == 0)
  1238.             continue;
  1239.  
  1240.         /* now handle various joints */
  1241.  
  1242.         if (ge->type == GE_LINE && ne->type == GE_LINE) {
  1243.             dx1 = ge->ix3 - ge->prev->ix3;
  1244.             dy1 = ge->iy3 - ge->prev->iy3;
  1245.             dx2 = ne->ix3 - ge->ix3;
  1246.             dy2 = ne->iy3 - ge->iy3;
  1247.  
  1248.             /* check whether they have the same direction */
  1249.             /* and the same slope */
  1250.             /* then we can join them into one line */
  1251.  
  1252.             if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0 && dx1 * dy2 == dy1 * dx2) {
  1253.                 /* extend the previous line */
  1254.                 ge->ix3 = ne->ix3;
  1255.                 ge->iy3 = ne->iy3;
  1256.  
  1257.                 /* and get rid of the next line */
  1258.                 freethisge(ne);
  1259.             }
  1260.         } else if (ge->type == GE_LINE && ne->type == GE_CURVE) {
  1261.             fixcvends(ne);
  1262.  
  1263.             dx1 = ge->ix3 - ge->prev->ix3;
  1264.             dy1 = ge->iy3 - ge->prev->iy3;
  1265.             dx2 = ne->ix1 - ge->ix3;
  1266.             dy2 = ne->iy1 - ge->iy3;
  1267.  
  1268.             /* if the line is nearly horizontal and we can fix it */
  1269.             if (dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0
  1270.                 && icheckcv(ne, 0, -dy1)
  1271.                 && abs(dy1) <= 4) {
  1272.                 dir = igetcvdir(ne);
  1273.                 ge->iy3 -= dy1;
  1274.                 ne->iy1 -= dy1;
  1275.                 fixcvdir(ne, dir);
  1276.                 if (ge->next != ne)
  1277.                     ne->prev->iy3 -= dy1;
  1278.                 dy1 = 0;
  1279.             } else if (dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0
  1280.                    && icheckcv(ne, -dx1, 0)
  1281.                    && abs(dx1) <= 4) {
  1282.                 /* the same but vertical */
  1283.                 dir = igetcvdir(ne);
  1284.                 ge->ix3 -= dx1;
  1285.                 ne->ix1 -= dx1;
  1286.                 fixcvdir(ne, dir);
  1287.                 if (ge->next != ne)
  1288.                     ne->prev->ix3 -= dx1;
  1289.                 dx1 = 0;
  1290.             }
  1291.             /*
  1292.              * if line is horizontal and curve begins nearly
  1293.              * horizontally
  1294.              */
  1295.             if (dy1 == 0 && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0) {
  1296.                 dir = igetcvdir(ne);
  1297.                 ne->iy1 -= dy2;
  1298.                 fixcvdir(ne, dir);
  1299.                 dy2 = 0;
  1300.             } else if (dx1 == 0 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0) {
  1301.                 /* the same but vertical */
  1302.                 dir = igetcvdir(ne);
  1303.                 ne->ix1 -= dx2;
  1304.                 fixcvdir(ne, dir);
  1305.                 dx2 = 0;
  1306.             }
  1307.         } else if (ge->type == GE_CURVE && ne->type == GE_LINE) {
  1308.             fixcvends(ge);
  1309.  
  1310.             dx1 = ge->ix3 - ge->ix2;
  1311.             dy1 = ge->iy3 - ge->iy2;
  1312.             dx2 = ne->ix3 - ge->ix3;
  1313.             dy2 = ne->iy3 - ge->iy3;
  1314.  
  1315.             /* if the line is nearly horizontal and we can fix it */
  1316.             if (dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0
  1317.                 && icheckcv(ge, 0, dy2)
  1318.                 && abs(dy2) <= 4) {
  1319.                 dir = igetcvdir(ge);
  1320.                 ge->iy3 += dy2;
  1321.                 ge->iy2 += dy2;
  1322.                 fixcvdir(ge, dir);
  1323.                 if (ge->next != ne)
  1324.                     ne->prev->iy3 += dy2;
  1325.                 dy2 = 0;
  1326.             } else if (dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0
  1327.                    && icheckcv(ge, dx2, 0)
  1328.                    && abs(dx2) <= 4) {
  1329.                 /* the same but vertical */
  1330.                 dir = igetcvdir(ge);
  1331.                 ge->ix3 += dx2;
  1332.                 ge->ix2 += dx2;
  1333.                 fixcvdir(ge, dir);
  1334.                 if (ge->next != ne)
  1335.                     ne->prev->ix3 += dx2;
  1336.                 dx2 = 0;
  1337.             }
  1338.             /*
  1339.              * if line is horizontal and curve ends nearly
  1340.              * horizontally
  1341.              */
  1342.             if (dy2 == 0 && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0) {
  1343.                 dir = igetcvdir(ge);
  1344.                 ge->iy2 += dy1;
  1345.                 fixcvdir(ge, dir);
  1346.                 dy1 = 0;
  1347.             } else if (dx2 == 0 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0) {
  1348.                 /* the same but vertical */
  1349.                 dir = igetcvdir(ge);
  1350.                 ge->ix2 += dx1;
  1351.                 fixcvdir(ge, dir);
  1352.                 dx1 = 0;
  1353.             }
  1354.         } else if (ge->type == GE_CURVE && ne->type == GE_CURVE) {
  1355.             fixcvends(ge);
  1356.             fixcvends(ne);
  1357.  
  1358.             dx1 = ge->ix3 - ge->ix2;
  1359.             dy1 = ge->iy3 - ge->iy2;
  1360.             dx2 = ne->ix1 - ge->ix3;
  1361.             dy2 = ne->iy1 - ge->iy3;
  1362.  
  1363.             /*
  1364.              * check if we have a rather smooth joint at extremal
  1365.              * point
  1366.              */
  1367.             /* left or right extremal point */
  1368.             if (abs(dx1) <= 4 && abs(dx2) <= 4
  1369.                 && dy1 != 0 && 5 * abs(dx1) / abs(dy1) == 0
  1370.                 && dy2 != 0 && 5 * abs(dx2) / abs(dy2) == 0
  1371.                 && (ge->iy3 < ge->prev->iy3 && ne->iy3 < ge->iy3
  1372.                 || ge->iy3 > ge->prev->iy3 && ne->iy3 > ge->iy3)
  1373.               && (ge->ix3 - ge->prev->ix3) * (ne->ix3 - ge->ix3) < 0
  1374.                 ) {
  1375.                 dir = igetcvdir(ge);
  1376.                 ge->ix2 += dx1;
  1377.                 dx1 = 0;
  1378.                 fixcvdir(ge, dir);
  1379.                 dir = igetcvdir(ne);
  1380.                 ne->ix1 -= dx2;
  1381.                 dx2 = 0;
  1382.                 fixcvdir(ne, dir);
  1383.             }
  1384.             /* top or down extremal point */
  1385.             else if (abs(dy1) <= 4 && abs(dy2) <= 4
  1386.                  && dx1 != 0 && 5 * abs(dy1) / abs(dx1) == 0
  1387.                  && dx2 != 0 && 5 * abs(dy2) / abs(dx2) == 0
  1388.                  && (ge->ix3 < ge->prev->ix3 && ne->ix3 < ge->ix3
  1389.                 || ge->ix3 > ge->prev->ix3 && ne->ix3 > ge->ix3)
  1390.                  && (ge->iy3 - ge->prev->iy3) * (ne->iy3 - ge->iy3) < 0
  1391.                 ) {
  1392.                 dir = igetcvdir(ge);
  1393.                 ge->iy2 += dy1;
  1394.                 dy1 = 0;
  1395.                 fixcvdir(ge, dir);
  1396.                 dir = igetcvdir(ne);
  1397.                 ne->iy1 -= dy2;
  1398.                 dy2 = 0;
  1399.                 fixcvdir(ne, dir);
  1400.             }
  1401.             /* or may be we just have a smooth junction */
  1402.             else if (dx1 * dx2 >= 0 && dy1 * dy2 >= 0
  1403.                  && 10 * abs(k = abs(dx1 * dy2) - abs(dy1 * dx2)) < (abs(dx1 * dy2) + abs(dy1 * dx2))) {
  1404.                 int             tries[6][4];
  1405.                 int             results[6];
  1406.                 int             i, b;
  1407.  
  1408.                 /* build array of changes we are going to try */
  1409.                 /* uninitalized entries are 0 */
  1410.                 if (k > 0) {
  1411.                     static int      t1[6][4] = {
  1412.                         {0, 0, 0, 0},
  1413.                         {-1, 0, 1, 0},
  1414.                         {-1, 0, 0, 1},
  1415.                         {0, -1, 1, 0},
  1416.                         {0, -1, 0, 1},
  1417.                     {-1, -1, 1, 1}};
  1418.                     memcpy(tries, t1, sizeof tries);
  1419.                 } else {
  1420.                     static int      t1[6][4] = {
  1421.                         {0, 0, 0, 0},
  1422.                         {1, 0, -1, 0},
  1423.                         {1, 0, 0, -1},
  1424.                         {0, 1, -1, 0},
  1425.                         {0, 1, 0, -1},
  1426.                     {1, 1, -1, -1}};
  1427.                     memcpy(tries, t1, sizeof tries);
  1428.                 }
  1429.  
  1430.                 /* now try the changes */
  1431.                 results[0] = abs(k);
  1432.                 for (i = 1; i < 6; i++) {
  1433.                     results[i] = abs((abs(dx1) + tries[i][0]) * (abs(dy2) + tries[i][1]) -
  1434.                              (abs(dy1) + tries[i][2]) * (abs(dx2) + tries[i][3]));
  1435.                 }
  1436.  
  1437.                 /* and find the best try */
  1438.                 k = abs(k);
  1439.                 b = 0;
  1440.                 for (i = 1; i < 6; i++)
  1441.                     if (results[i] < k) {
  1442.                         k = results[i];
  1443.                         b = i;
  1444.                     }
  1445.                 /* and finally apply it */
  1446.                 if (dx1 < 0)
  1447.                     tries[b][0] = -tries[b][0];
  1448.                 if (dy2 < 0)
  1449.                     tries[b][1] = -tries[b][1];
  1450.                 if (dy1 < 0)
  1451.                     tries[b][2] = -tries[b][2];
  1452.                 if (dx2 < 0)
  1453.                     tries[b][3] = -tries[b][3];
  1454.  
  1455.                 dir = igetcvdir(ge);
  1456.                 ge->ix2 -= tries[b][0];
  1457.                 ge->iy2 -= tries[b][2];
  1458.                 fixcvdir(ge, dir);
  1459.                 dir = igetcvdir(ne);
  1460.                 ne->ix1 += tries[b][3];
  1461.                 ne->iy1 += tries[b][1];
  1462.                 fixcvdir(ne, dir);
  1463.             }
  1464.         }
  1465.     }
  1466. }
  1467.  
  1468. /* debugging: print out stems of a glyph */
  1469. static void
  1470. debugstems(
  1471.        char *name,
  1472.        STEM * hstems,
  1473.        int nhs,
  1474.        STEM * vstems,
  1475.        int nvs
  1476. )
  1477. {
  1478.     int             i;
  1479.  
  1480.     fprintf(pfa_file, "%% %s\n", name);
  1481.     fprintf(pfa_file, "%% %d horizontal stems:\n", nhs);
  1482.     for (i = 0; i < nhs; i++)
  1483.         fprintf(pfa_file, "%% %3d    %d (%d...%d) %c %c%c%c%c\n", i, hstems[i].value,
  1484.             hstems[i].from, hstems[i].to,
  1485.             ((hstems[i].flags & ST_UP) ? 'U' : 'D'),
  1486.             ((hstems[i].flags & ST_END) ? 'E' : '-'),
  1487.             ((hstems[i].flags & ST_FLAT) ? 'F' : '-'),
  1488.             ((hstems[i].flags & ST_ZONE) ? 'Z' : ' '),
  1489.             ((hstems[i].flags & ST_TOPZONE) ? 'T' : ' '));
  1490.     fprintf(pfa_file, "%% %d vertical stems:\n", nvs);
  1491.     for (i = 0; i < nvs; i++)
  1492.         fprintf(pfa_file, "%% %3d    %d (%d...%d) %c %c%c\n", i, vstems[i].value,
  1493.             vstems[i].from, vstems[i].to,
  1494.             ((vstems[i].flags & ST_UP) ? 'U' : 'D'),
  1495.             ((vstems[i].flags & ST_END) ? 'E' : '-'),
  1496.             ((vstems[i].flags & ST_FLAT) ? 'F' : '-'));
  1497. }
  1498.  
  1499. /* add pseudo-stems for the limits of the Blue zones to the stem array */
  1500. static int
  1501. addbluestems(
  1502.     STEM *s,
  1503.     int n
  1504. )
  1505. {
  1506.     int i;
  1507.  
  1508.     for(i=0; i<nblues && i<2; i+=2) { /* baseline */
  1509.         s[n].value=bluevalues[i];
  1510.         s[n].flags=ST_UP|ST_ZONE;
  1511.         /* don't overlap with anything */
  1512.         s[n].origin=s[n].from=s[n].to= -10000+i;
  1513.         n++;
  1514.         s[n].value=bluevalues[i+1];
  1515.         s[n].flags=ST_ZONE;
  1516.         /* don't overlap with anything */
  1517.         s[n].origin=s[n].from=s[n].to= -10000+i+1;
  1518.         n++;
  1519.     }
  1520.     for(i=2; i<nblues; i+=2) { /* top zones */
  1521.         s[n].value=bluevalues[i];
  1522.         s[n].flags=ST_UP|ST_ZONE|ST_TOPZONE;
  1523.         /* don't overlap with anything */
  1524.         s[n].origin=s[n].from=s[n].to= -10000+i;
  1525.         n++;
  1526.         s[n].value=bluevalues[i+1];
  1527.         s[n].flags=ST_ZONE|ST_TOPZONE;
  1528.         /* don't overlap with anything */
  1529.         s[n].origin=s[n].from=s[n].to= -10000+i+1;
  1530.         n++;
  1531.     }
  1532.     for(i=0; i<notherb; i+=2) { /* bottom zones */
  1533.         s[n].value=otherblues[i];
  1534.         s[n].flags=ST_UP|ST_ZONE;
  1535.         /* don't overlap with anything */
  1536.         s[n].origin=s[n].from=s[n].to= -10000+i+nblues;
  1537.         n++;
  1538.         s[n].value=otherblues[i+1];
  1539.         s[n].flags=ST_ZONE;
  1540.         /* don't overlap with anything */
  1541.         s[n].origin=s[n].from=s[n].to= -10000+i+1+nblues;
  1542.         n++;
  1543.     }
  1544.     return n;
  1545. }
  1546.  
  1547. /* sort stems in array */
  1548. static void
  1549. sortstems(
  1550.       STEM * s,
  1551.       int n
  1552. )
  1553. {
  1554.     int             i, j;
  1555.     STEM            x;
  1556.  
  1557.  
  1558.     /* a simple sorting */
  1559.     /* hm, the ordering criteria are not quite simple :-) 
  1560.      * if the values are tied
  1561.      * ST_UP always goes under not ST_UP
  1562.      * ST_ZONE goes on the most outer side
  1563.      * ST_END goes towards inner side after ST_ZONE
  1564.      * ST_FLAT goes on the inner side
  1565.      */
  1566.  
  1567.     for (i = 0; i < n; i++)
  1568.         for (j = i + 1; j < n; j++) {
  1569.             if(s[i].value < s[j].value)
  1570.                 continue;
  1571.             if(s[i].value == s[j].value) {
  1572.                 if( (s[i].flags & ST_UP) < (s[j].flags & ST_UP) )
  1573.                     continue;
  1574.                 if( (s[i].flags & ST_UP) == (s[j].flags & ST_UP) ) {
  1575.                     if( s[i].flags & ST_UP ) {
  1576.                         if(
  1577.                         (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
  1578.                             >
  1579.                         (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
  1580.                         )
  1581.                             continue;
  1582.                     } else {
  1583.                         if(
  1584.                         (s[i].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
  1585.                             <
  1586.                         (s[j].flags & (ST_ZONE|ST_FLAT|ST_END) ^ ST_FLAT)
  1587.                         )
  1588.                             continue;
  1589.                     }
  1590.                 }
  1591.             }
  1592.             x = s[j];
  1593.             s[j] = s[i];
  1594.             s[i] = x;
  1595.         }
  1596. }
  1597.  
  1598. /* check whether two stem borders overlap */
  1599.  
  1600. static int
  1601. stemoverlap(
  1602.         STEM * s1,
  1603.         STEM * s2
  1604. )
  1605. {
  1606.     int             result;
  1607.  
  1608.     if (s1->from <= s2->from && s1->to >= s2->from
  1609.         || s2->from <= s1->from && s2->to >= s1->from)
  1610.         result = 1;
  1611.     else
  1612.         result = 0;
  1613.  
  1614.     if (ISDBG(STEMOVERLAP))
  1615.         fprintf(pfa_file, "%% overlap %d(%d..%d)x%d(%d..%d)=%d\n",
  1616.             s1->value, s1->from, s1->to, s2->value, s2->from, s2->to, result);
  1617.     return result;
  1618. }
  1619.  
  1620. /* 
  1621.  * check if the stem [border] is in an appropriate blue zone
  1622.  * (currently not used)
  1623.  */
  1624.  
  1625. static int
  1626. steminblue(
  1627.     STEM *s
  1628. )
  1629. {
  1630.     int i, val;
  1631.  
  1632.     val=s->value;
  1633.     if(s->flags & ST_UP) {
  1634.         /* painted size up, look at lower zones */
  1635.         if(nblues>=2 && val>=bluevalues[0] && val<=bluevalues[1] )
  1636.             return 1;
  1637.         for(i=0; i<notherb; i++) {
  1638.             if( val>=otherblues[i] && val<=otherblues[i+1] )
  1639.                 return 1;
  1640.         }
  1641.     } else {
  1642.         /* painted side down, look at upper zones */
  1643.         for(i=2; i<nblues; i++) {
  1644.             if( val>=bluevalues[i] && val<=bluevalues[i+1] )
  1645.                 return 1;
  1646.         }
  1647.     }
  1648.  
  1649.     return 0;
  1650. }
  1651.  
  1652. /* mark the outermost stem [borders] in the blue zones */
  1653.  
  1654. static void
  1655. markbluestems(
  1656.     STEM *s,
  1657.     int nold
  1658. )
  1659. {
  1660.     int i, j, a, b, c;
  1661.     /*
  1662.      * traverse the list of Blue Values, mark the lowest upper
  1663.      * stem in each bottom zone and the topmost lower stem in
  1664.      * each top zone with ST_BLUE
  1665.      */
  1666.  
  1667.     /* top zones */
  1668.     for(i=2; i<nblues; i+=2) {
  1669.         a=bluevalues[i]; b=bluevalues[i+1];
  1670.         if(ISDBG(BLUESTEMS))
  1671.             fprintf(pfa_file, "%% looking at blue zone %d...%d\n", a, b);
  1672.         for(j=nold-1; j>=0; j--) {
  1673.             if( s[j].flags & (ST_ZONE|ST_UP|ST_END) )
  1674.                 continue;
  1675.             c=s[j].value;
  1676.             if(c<a) /* too low */
  1677.                 break;
  1678.             if(c<=b) { /* found the topmost stem border */
  1679.                 /* mark all the stems with the same value */
  1680.                 if(ISDBG(BLUESTEMS))
  1681.                     fprintf(pfa_file, "%% found D BLUE at %d\n", s[j].value);
  1682.                 /* include ST_END values */
  1683.                 while( s[j+1].value==c && (s[j+1].flags & ST_ZONE)==0 )
  1684.                     j++;
  1685.                 s[j].flags |= ST_BLUE;
  1686.                 for(j--; j>=0 && s[j].value==c 
  1687.                         && (s[j].flags & (ST_UP|ST_ZONE))==0 ; j--)
  1688.                     s[j].flags |= ST_BLUE;
  1689.                 break;
  1690.             }
  1691.         }
  1692.     }
  1693.     /* baseline */
  1694.     if(nblues>=2) {
  1695.         a=bluevalues[0]; b=bluevalues[1];
  1696.         for(j=0; j<nold; j++) {
  1697.             if( (s[j].flags & (ST_ZONE|ST_UP|ST_END))!=ST_UP )
  1698.                 continue;
  1699.             c=s[j].value;
  1700.             if(c>b) /* too high */
  1701.                 break;
  1702.             if(c>=a) { /* found the lowest stem border */
  1703.                 /* mark all the stems with the same value */
  1704.                 if(ISDBG(BLUESTEMS))
  1705.                     fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value);
  1706.                 /* include ST_END values */
  1707.                 while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 )
  1708.                     j--;
  1709.                 s[j].flags |= ST_BLUE;
  1710.                 for(j++; j<nold && s[j].value==c
  1711.                         && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
  1712.                     s[j].flags |= ST_BLUE;
  1713.                 break;
  1714.             }
  1715.         }
  1716.     }
  1717.     /* bottom zones: the logic is the same as for baseline */
  1718.     for(i=0; i<notherb; i+=2) {
  1719.         a=otherblues[i]; b=otherblues[i+1];
  1720.         for(j=0; j<nold; j++) {
  1721.             if( (s[j].flags & (ST_UP|ST_ZONE|ST_END))!=ST_UP )
  1722.                 continue;
  1723.             c=s[j].value;
  1724.             if(c>b) /* too high */
  1725.                 break;
  1726.             if(c>=a) { /* found the lowest stem border */
  1727.                 /* mark all the stems with the same value */
  1728.                 if(ISDBG(BLUESTEMS))
  1729.                     fprintf(pfa_file, "%% found U BLUE at %d\n", s[j].value);
  1730.                 /* include ST_END values */
  1731.                 while( s[j-1].value==c && (s[j-1].flags & ST_ZONE)==0 )
  1732.                     j--;
  1733.                 s[j].flags |= ST_BLUE;
  1734.                 for(j++; j<nold && s[j].value==c
  1735.                         && (s[j].flags & (ST_UP|ST_ZONE))==ST_UP ; j++)
  1736.                     s[j].flags |= ST_BLUE;
  1737.                 break;
  1738.             }
  1739.         }
  1740.     }
  1741. }
  1742.  
  1743. /* Eliminate invalid stems, join equivalent lines and remove nested stems
  1744.  * to build the main (non-substituted) set of stems.
  1745.  * XXX add consideration of the italic angle
  1746.  */
  1747. static int
  1748. joinmainstems(
  1749.       STEM * s,
  1750.       int nold,
  1751.       int useblues /* do we use the blue values ? */
  1752. )
  1753. {
  1754. #define MAX_STACK    1000
  1755.     STEM            stack[MAX_STACK];
  1756.     int             nstack = 0;
  1757.     int             sbottom = 0;
  1758.     int             nnew;
  1759.     int             i, j, k;
  1760.     int             a, b, c, w1, w2, w3;
  1761.     int             fw, fd;
  1762.     /*
  1763.      * priority of the last found stem: 
  1764.      * 0 - nothing found yet 
  1765.      * 1 - has ST_END in it (one or more) 
  1766.      * 2 - has no ST_END and no ST_FLAT, can override only one stem 
  1767.      *     with priority 1 
  1768.      * 3 - has no ST_END and at least one ST_FLAT, can override one 
  1769.      *     stem with priority 2 or any number of stems with priority 1
  1770.      * 4 (handled separately) - has ST_BLUE, can override anything
  1771.      */
  1772.     int             readystem = 0;
  1773.     int             pri;
  1774.     int             nlps = 0;    /* number of non-committed
  1775.                      * lowest-priority stems */
  1776.  
  1777.  
  1778.     for (i = 0, nnew = 0; i < nold; i++) {
  1779.         if (s[i].flags & (ST_UP|ST_ZONE)) {
  1780.             if(s[i].flags & ST_BLUE) {
  1781.                 /* we just HAVE to use this value */
  1782.                 if (readystem)
  1783.                     nnew += 2;
  1784.                 readystem=0;
  1785.  
  1786.                 /* remember the list of Blue zone stems with the same value */
  1787.                 for(a=i, i++; i<nold && s[a].value==s[i].value
  1788.                     && (s[i].flags & ST_BLUE); i++)
  1789.                     {}
  1790.                 b=i; /* our range is a <= i < b */
  1791.                 c= -1; /* index of our best guess up to now */
  1792.                 pri=0;
  1793.                 /* try to find a match, don't cross blue zones */
  1794.                 for(; i<nold && (s[i].flags & ST_BLUE)==0; i++) {
  1795.                     if(s[i].flags & ST_UP) {
  1796.                         if(s[i].flags & ST_TOPZONE)
  1797.                             break;
  1798.                         else
  1799.                             continue;
  1800.                     }
  1801.                     for(j=a; j<b; j++) {
  1802.                         if(!stemoverlap(&s[j], &s[i]) )
  1803.                             continue;
  1804.                         /* consider priorities */
  1805.                         if( ( (s[j].flags|s[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
  1806.                             c=i;
  1807.                             goto bluematch;
  1808.                         }
  1809.                         if( ((s[j].flags|s[i].flags) & ST_END)==0 )  {
  1810.                             if(pri < 2) {
  1811.                                 c=i; pri=2;
  1812.                             }
  1813.                         } else {
  1814.                             if(pri == 0) {
  1815.                                 c=i; pri=1;
  1816.                             }
  1817.                         }
  1818.                     }
  1819.                 }
  1820.             bluematch:
  1821.                 /* clean up the stack */
  1822.                 nstack=sbottom=0;
  1823.                 readystem=0;
  1824.                 /* add this stem */
  1825.                 s[nnew++]=s[a];
  1826.                 if(c<0) { /* make one-dot-wide stem */
  1827.                     if(nnew>=b) { /* have no free space */
  1828.                         for(j=nold; j>=b; j--) /* make free space */
  1829.                             s[j]=s[j-1];
  1830.                         b++;
  1831.                         nold++;
  1832.                     }
  1833.                     s[nnew]=s[a];
  1834.                     s[nnew].flags &= ~(ST_UP|ST_BLUE);
  1835.                     nnew++;
  1836.                     i=b-1;
  1837.                 } else {
  1838.                     s[nnew++]=s[c];
  1839.                     i=c; /* skip up to this point */
  1840.                 }
  1841.                 if (ISDBG(MAINSTEMS))
  1842.                     fprintf(pfa_file, "%% +stem %d...%d U BLUE\n",
  1843.                         s[nnew-2].value, s[nnew-1].value);
  1844.             } else {
  1845.                 if (nstack >= MAX_STACK) {
  1846.                     WARNING_1 fprintf(stderr, "Warning: **** converter's stem stack overflow ****\n");
  1847.                     nstack = 0;
  1848.                 }
  1849.                 stack[nstack++] = s[i];
  1850.             }
  1851.         } else if(s[i].flags & ST_BLUE) {
  1852.             /* again, we just HAVE to use this value */
  1853.             if (readystem)
  1854.                 nnew += 2;
  1855.             readystem=0;
  1856.  
  1857.             /* remember the list of Blue zone stems with the same value */
  1858.             for(a=i, i++; i<nold && s[a].value==s[i].value
  1859.                 && (s[i].flags & ST_BLUE); i++)
  1860.                 {}
  1861.             b=i; /* our range is a <= i < b */
  1862.             c= -1; /* index of our best guess up to now */
  1863.             pri=0;
  1864.             /* try to find a match */
  1865.             for (i = nstack - 1; i >= 0; i--) {
  1866.                 if( (stack[i].flags & ST_UP)==0 ) {
  1867.                     if( (stack[i].flags & (ST_ZONE|ST_TOPZONE))==ST_ZONE )
  1868.                         break;
  1869.                     else
  1870.                         continue;
  1871.                 }
  1872.                 for(j=a; j<b; j++) {
  1873.                     if(!stemoverlap(&s[j], &stack[i]) )
  1874.                         continue;
  1875.                     /* consider priorities */
  1876.                     if( ( (s[j].flags|stack[i].flags) & (ST_FLAT|ST_END) )==ST_FLAT ) {
  1877.                         c=i;
  1878.                         goto bluedownmatch;
  1879.                     }
  1880.                     if( ((s[j].flags|stack[i].flags) & ST_END)==0 )  {
  1881.                         if(pri < 2) {
  1882.                             c=i; pri=2;
  1883.                         }
  1884.                     } else {
  1885.                         if(pri == 0) {
  1886.                             c=i; pri=1;
  1887.                         }
  1888.                     }
  1889.                 }
  1890.             }
  1891.         bluedownmatch:
  1892.             /* if found no match make a one-dot-wide stem */
  1893.             if(c<0) {
  1894.                 c=0;
  1895.                 stack[0]=s[b-1];
  1896.                 stack[0].flags |= ST_UP;
  1897.                 stack[0].flags &= ~ST_BLUE;
  1898.             }
  1899.             /* remove all the stems conflicting with this one */
  1900.             readystem=0;
  1901.             for(j=nnew-2; j>=0; j-=2) {
  1902.                 if (ISDBG(MAINSTEMS))
  1903.                     fprintf(pfa_file, "%% ?stem %d...%d -- %d\n",
  1904.                         s[j].value, s[j+1].value, stack[c].value);
  1905.                 if(s[j+1].value < stack[c].value) /* no conflict */
  1906.                     break;
  1907.                 if(s[j].flags & ST_BLUE) {
  1908.                     /* oops, we don't want to spoil other blue zones */
  1909.                     stack[c].value=s[j+1].value+1;
  1910.                     break;
  1911.                 }
  1912.                 if( (s[j].flags|s[j+1].flags) & ST_END ) {
  1913.                     if (ISDBG(MAINSTEMS))
  1914.                         fprintf(pfa_file, "%% -stem %d...%d p=1\n",
  1915.                             s[j].value, s[j+1].value);
  1916.                     continue; /* pri==1, silently discard it */
  1917.                 }
  1918.                 /* we want to discard no nore than 2 stems of pri>=2 */
  1919.                 if( ++readystem > 2 ) {
  1920.                     /* change our stem to not conflict */
  1921.                     stack[c].value=s[j+1].value+1;
  1922.                     break;
  1923.                 } else {
  1924.                     if (ISDBG(MAINSTEMS))
  1925.                         fprintf(pfa_file, "%% -stem %d...%d p>=2\n",
  1926.                             s[j].value, s[j+1].value);
  1927.                     continue;
  1928.                 }
  1929.             }
  1930.             nnew=j+2;
  1931.             /* add this stem */
  1932.             if(nnew>=b-1) { /* have no free space */
  1933.                 for(j=nold; j>=b-1; j--) /* make free space */
  1934.                     s[j]=s[j-1];
  1935.                 b++;
  1936.                 nold++;
  1937.             }
  1938.             s[nnew++]=stack[c];
  1939.             s[nnew++]=s[b-1];
  1940.             /* clean up the stack */
  1941.             nstack=sbottom=0;
  1942.             readystem=0;
  1943.             /* set the next position to search */
  1944.             i=b-1;
  1945.             if (ISDBG(MAINSTEMS))
  1946.                 fprintf(pfa_file, "%% +stem %d...%d D BLUE\n",
  1947.                     s[nnew-2].value, s[nnew-1].value);
  1948.         } else if (nstack > 0) {
  1949.  
  1950.             /*
  1951.              * check whether our stem overlaps with anything in
  1952.              * stack
  1953.              */
  1954.             for (j = nstack - 1; j >= sbottom; j--) {
  1955.                 if (s[i].value <= stack[j].value)
  1956.                     break;
  1957.                 if (stack[j].flags & ST_ZONE)
  1958.                     continue;
  1959.  
  1960.                 if ((s[i].flags & ST_END)
  1961.                     || (stack[j].flags & ST_END))
  1962.                     pri = 1;
  1963.                 else if ((s[i].flags & ST_FLAT)
  1964.                      || (stack[j].flags & ST_FLAT))
  1965.                     pri = 3;
  1966.                 else
  1967.                     pri = 2;
  1968.  
  1969.                 if (pri < readystem && s[nnew + 1].value >= stack[j].value
  1970.                     || !stemoverlap(&stack[j], &s[i]))
  1971.                     continue;
  1972.  
  1973.                 if (readystem > 1 && s[nnew + 1].value < stack[j].value) {
  1974.                     nnew += 2;
  1975.                     readystem = 0;
  1976.                     nlps = 0;
  1977.                 }
  1978.                 /*
  1979.                  * width of the previous stem (if it's
  1980.                  * present)
  1981.                  */
  1982.                 w1 = s[nnew + 1].value - s[nnew].value;
  1983.  
  1984.                 /* width of this stem */
  1985.                 w2 = s[i].value - stack[j].value;
  1986.  
  1987.                 if (readystem == 0) {
  1988.                     /* nothing yet, just add a new stem */
  1989.                     s[nnew] = stack[j];
  1990.                     s[nnew + 1] = s[i];
  1991.                     readystem = pri;
  1992.                     if (pri == 1)
  1993.                         nlps = 1;
  1994.                     else if (pri == 2)
  1995.                         sbottom = j;
  1996.                     else {
  1997.                         sbottom = j + 1;
  1998.                         while (sbottom < nstack
  1999.                                && stack[sbottom].value <= stack[j].value)
  2000.                             sbottom++;
  2001.                     }
  2002.                     if (ISDBG(MAINSTEMS))
  2003.                         fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
  2004.                             stack[j].value, s[i].value, pri, nlps);
  2005.                 } else if (pri == 1) {
  2006.                     if (stack[j].value > s[nnew + 1].value) {
  2007.                         /*
  2008.                          * doesn't overlap with the
  2009.                          * previous one
  2010.                          */
  2011.                         nnew += 2;
  2012.                         nlps++;
  2013.                         s[nnew] = stack[j];
  2014.                         s[nnew + 1] = s[i];
  2015.                         if (ISDBG(MAINSTEMS))
  2016.                             fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
  2017.                                 stack[j].value, s[i].value, pri, nlps);
  2018.                     } else if (w2 < w1) {
  2019.                         /* is narrower */
  2020.                         s[nnew] = stack[j];
  2021.                         s[nnew + 1] = s[i];
  2022.                         if (ISDBG(MAINSTEMS))
  2023.                             fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d %d->%d\n",
  2024.                                 stack[j].value, s[i].value, pri, nlps, w1, w2);
  2025.                     }
  2026.                 } else if (pri == 2) {
  2027.                     if (readystem == 2) {
  2028.                         /* choose the narrower stem */
  2029.                         if (w1 > w2) {
  2030.                             s[nnew] = stack[j];
  2031.                             s[nnew + 1] = s[i];
  2032.                             sbottom = j;
  2033.                             if (ISDBG(MAINSTEMS))
  2034.                                 fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
  2035.                                     stack[j].value, s[i].value, pri, nlps);
  2036.                         }
  2037.                         /* else readystem==1 */
  2038.                     } else if (stack[j].value > s[nnew + 1].value) {
  2039.                         /*
  2040.                          * value doesn't overlap with
  2041.                          * the previous one
  2042.                          */
  2043.                         nnew += 2;
  2044.                         nlps = 0;
  2045.                         s[nnew] = stack[j];
  2046.                         s[nnew + 1] = s[i];
  2047.                         sbottom = j;
  2048.                         readystem = pri;
  2049.                         if (ISDBG(MAINSTEMS))
  2050.                             fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
  2051.                                 stack[j].value, s[i].value, pri, nlps);
  2052.                     } else if (nlps == 1
  2053.                            || stack[j].value > s[nnew - 1].value) {
  2054.                         /*
  2055.                          * we can replace the top
  2056.                          * stem
  2057.                          */
  2058.                         nlps = 0;
  2059.                         s[nnew] = stack[j];
  2060.                         s[nnew + 1] = s[i];
  2061.                         readystem = pri;
  2062.                         sbottom = j;
  2063.                         if (ISDBG(MAINSTEMS))
  2064.                             fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
  2065.                                 stack[j].value, s[i].value, pri, nlps);
  2066.                     }
  2067.                 } else if (readystem == 3) {    /* that means also
  2068.                                  * pri==3 */
  2069.                     /* choose the narrower stem */
  2070.                     if (w1 > w2) {
  2071.                         s[nnew] = stack[j];
  2072.                         s[nnew + 1] = s[i];
  2073.                         sbottom = j + 1;
  2074.                         while (sbottom < nstack
  2075.                                && stack[sbottom].value <= stack[j].value)
  2076.                             sbottom++;
  2077.                         if (ISDBG(MAINSTEMS))
  2078.                             fprintf(pfa_file, "%% /stem %d...%d p=%d n=%d\n",
  2079.                                 stack[j].value, s[i].value, pri, nlps);
  2080.                     }
  2081.                 } else if (pri == 3) {
  2082.                     /*
  2083.                      * we can replace as many stems as
  2084.                      * neccessary
  2085.                      */
  2086.                     nnew += 2;
  2087.                     while (nnew > 0 && s[nnew - 1].value >= stack[j].value) {
  2088.                         nnew -= 2;
  2089.                         if (ISDBG(MAINSTEMS))
  2090.                             fprintf(pfa_file, "%% -stem %d..%d\n",
  2091.                                 s[nnew].value, s[nnew + 1].value);
  2092.                     }
  2093.                     nlps = 0;
  2094.                     s[nnew] = stack[j];
  2095.                     s[nnew + 1] = s[i];
  2096.                     readystem = pri;
  2097.                     sbottom = j + 1;
  2098.                     while (sbottom < nstack
  2099.                            && stack[sbottom].value <= stack[j].value)
  2100.                         sbottom++;
  2101.                     if (ISDBG(MAINSTEMS))
  2102.                         fprintf(pfa_file, "%% +stem %d...%d p=%d n=%d\n",
  2103.                             stack[j].value, s[i].value, pri, nlps);
  2104.                 }
  2105.             }
  2106.         }
  2107.     }
  2108.     if (readystem)
  2109.         nnew += 2;
  2110.  
  2111.     /* change the 1-pixel-wide stems to 20-pixel-wide stems if possible 
  2112.      * the constant 20 is recommended in the Type1 manual 
  2113.      */
  2114.     if(useblues) {
  2115.         for(i=0; i<nnew; i+=2) {
  2116.             if(s[i].value != s[i+1].value)
  2117.                 continue;
  2118.             if( ((s[i].flags ^ s[i+1].flags) & ST_BLUE)==0 )
  2119.                 continue;
  2120.             if( s[i].flags & ST_BLUE ) {
  2121.                 if(nnew>i+2 && s[i+2].value<s[i].value+22)
  2122.                     s[i+1].value=s[i+2].value-2; /* compensate for fuzziness */
  2123.                 else
  2124.                     s[i+1].value+=20;
  2125.             } else {
  2126.                 if(i>0 && s[i-1].value>s[i].value-22)
  2127.                     s[i].value=s[i-1].value+2; /* compensate for fuzziness */
  2128.                 else
  2129.                     s[i].value-=20;
  2130.             }
  2131.         }
  2132.     }
  2133.     /* make sure that no stem it stretched between
  2134.      * a top zone and a bottom zone
  2135.      */
  2136.     if(useblues) {
  2137.         for(i=0; i<nnew; i+=2) {
  2138.             a=10000; /* lowest border of top zone crosing the stem */
  2139.             b= -10000; /* highest border of bottom zone crossing the stem */
  2140.  
  2141.             for(j=2; j<nblues; j++) {
  2142.                 c=bluevalues[j];
  2143.                 if( c>=s[i].value && c<=s[i+1].value && c<a )
  2144.                     a=c;
  2145.             }
  2146.             if(nblues>=2) {
  2147.                 c=bluevalues[1];
  2148.                 if( c>=s[i].value && c<=s[i+1].value && c>b )
  2149.                     b=c;
  2150.             }
  2151.             for(j=1; j<notherb; j++) {
  2152.                 c=otherblues[j];
  2153.                 if( c>=s[i].value && c<=s[i+1].value && c>b )
  2154.                     b=c;
  2155.             }
  2156.             if( a!=10000 && b!= -10000 ) { /* it is stretched */
  2157.                 /* split the stem into 2 ghost stems */
  2158.                 for(j=nnew+1; j>i+1; j--) /* make free space */
  2159.                     s[j]=s[j-2];
  2160.                 nnew+=2;
  2161.  
  2162.                 if(s[i].value+22 >= a)
  2163.                     s[i+1].value=a-2; /* leave space for fuzziness */
  2164.                 else
  2165.                     s[i+1].value=s[i].value+20;
  2166.  
  2167.                 if(s[i+3].value-22 <= b)
  2168.                     s[i+2].value=b+2; /* leave space for fuzziness */
  2169.                 else
  2170.                     s[i+2].value=s[i+3].value-20;
  2171.  
  2172.                 i+=2;
  2173.             }
  2174.         }
  2175.     }
  2176.     /* look for triple stems */
  2177.     for (i = 0; i < nnew; i += 2) {
  2178.         if (nnew - i >= 6) {
  2179.             a = s[i].value + s[i + 1].value;
  2180.             b = s[i + 2].value + s[i + 3].value;
  2181.             c = s[i + 4].value + s[i + 5].value;
  2182.  
  2183.             w1 = s[i + 1].value - s[i].value;
  2184.             w2 = s[i + 3].value - s[i + 2].value;
  2185.             w3 = s[i + 5].value - s[i + 4].value;
  2186.  
  2187.             fw = w3 - w1;    /* fuzz in width */
  2188.             fd = ((c - b) - (b - a));    /* fuzz in distance
  2189.                              * (doubled) */
  2190.  
  2191.             /* we are able to handle some fuzz */
  2192.             /*
  2193.              * it doesn't hurt if the declared stem is a bit
  2194.              * narrower than actual unless it's an edge in
  2195.              * a blue zone
  2196.              */
  2197.             if (abs(abs(fd) - abs(fw)) * 5 < w2
  2198.                 && abs(fw) * 20 < (w1 + w3)) {    /* width dirrerence <10% */
  2199.  
  2200.                 if(useblues) { /* check that we don't disturb any blue stems */
  2201.                     j=c; k=a;
  2202.                     if (fw > 0) {
  2203.                         if (fd > 0) {
  2204.                             if( s[i+5].flags & ST_BLUE )
  2205.                                 continue;
  2206.                             j -= fw;
  2207.                         } else {
  2208.                             if( s[i+4].flags & ST_BLUE )
  2209.                                 continue;
  2210.                             j += fw;
  2211.                         }
  2212.                     } else if(fw < 0) {
  2213.                         if (fd > 0) {
  2214.                             if( s[i+1].flags & ST_BLUE )
  2215.                                 continue;
  2216.                             k -= fw;
  2217.                         } else {
  2218.                             if( s[i].flags & ST_BLUE )
  2219.                                 continue;
  2220.                             k += fw;
  2221.                         }
  2222.                     }
  2223.                     pri = ((j - b) - (b - k));
  2224.  
  2225.                     if (pri > 0) {
  2226.                         if( s[i+2].flags & ST_BLUE )
  2227.                             continue;
  2228.                     } else if(pri < 0) {
  2229.                         if( s[i+3].flags & ST_BLUE )
  2230.                             continue;
  2231.                     }
  2232.                 }
  2233.  
  2234.                 /*
  2235.                  * first fix up the width of 1st and 3rd
  2236.                  * stems
  2237.                  */
  2238.                 if (fw > 0) {
  2239.                     if (fd > 0) {
  2240.                         s[i + 5].value -= fw;
  2241.                         c -= fw;
  2242.                     } else {
  2243.                         s[i + 4].value += fw;
  2244.                         c += fw;
  2245.                     }
  2246.                 } else {
  2247.                     if (fd > 0) {
  2248.                         s[i + 1].value -= fw;
  2249.                         a -= fw;
  2250.                     } else {
  2251.                         s[i].value += fw;
  2252.                         a += fw;
  2253.                     }
  2254.                 }
  2255.                 fd = ((c - b) - (b - a));
  2256.  
  2257.                 if (fd > 0) {
  2258.                     s[i + 2].value += abs(fd) / 2;
  2259.                 } else {
  2260.                     s[i + 3].value -= abs(fd) / 2;
  2261.                 }
  2262.  
  2263.                 s[i].flags |= ST_3;
  2264.                 i += 4;
  2265.             }
  2266.         }
  2267.     }
  2268.  
  2269.     return (nnew & ~1);    /* number of lines must be always even */
  2270. }
  2271.  
  2272. /*
  2273.  * these macros and function allow to set the base stem,
  2274.  * check that it's not empty and subtract another stem
  2275.  * from the base stem (possibly dividing it into multiple parts)
  2276.  */
  2277.  
  2278. /* pairs for pieces of the base stem */
  2279. static short xbstem[MAX_STEMS*2]; 
  2280. /* index of the last point */
  2281. static int xblast= -1; 
  2282.  
  2283. #define setbasestem(from, to) \
  2284.     (xbstem[0]=from, xbstem[1]=to, xblast=1)
  2285. #define isbaseempty()    (xblast<=0)
  2286.  
  2287. /* returns 1 if was overlapping, 0 otherwise */
  2288. static int
  2289. subfrombase(
  2290.     int from,
  2291.     int to
  2292. {
  2293.     int a, b;
  2294.     int i, j;
  2295.  
  2296.     if(isbaseempty())
  2297.         return 0;
  2298.  
  2299.     /* handle the simple case simply */
  2300.     if(from > xbstem[xblast] || to < xbstem[0])
  2301.         return 0;
  2302.  
  2303.     /* the binary search may be more efficient */
  2304.     /* but for now the linear search is OK */
  2305.     for(b=1; from > xbstem[b]; b+=2) {} /* result: from <= xbstem[b] */
  2306.     for(a=xblast-1; to < xbstem[a]; a-=2) {} /* result: to >= xbstem[a] */
  2307.  
  2308.     /* now the interesting examples are:
  2309.      * (it was hard for me to understand, so I looked at the examples)
  2310.      * 1
  2311.      *     a|-----|          |-----|b   |-----|     |-----|
  2312.      *              f|-----|t
  2313.      * 2
  2314.      *     a|-----|b         |-----|    |-----|     |-----|
  2315.      *      f|--|t
  2316.      * 3
  2317.      *     a|-----|b         |-----|    |-----|     |-----|
  2318.      *           f|-----|t
  2319.      * 4
  2320.      *      |-----|b        a|-----|    |-----|     |-----|
  2321.      *          f|------------|t
  2322.      * 5
  2323.      *      |-----|          |-----|b   |-----|    a|-----|
  2324.      *                   f|-----------------------------|t
  2325.      * 6
  2326.      *      |-----|b         |-----|    |-----|    a|-----|
  2327.      *   f|--------------------------------------------------|t
  2328.      * 7
  2329.      *      |-----|b         |-----|   a|-----|     |-----|
  2330.      *          f|--------------------------|t
  2331.      */
  2332.  
  2333.     if(a < b-1) /* hits a gap  - example 1 */
  2334.         return 0;
  2335.  
  2336.     /* now the subtraction itself */
  2337.  
  2338.     if(a==b-1 && from > xbstem[a] && to < xbstem[b]) {
  2339.         /* overlaps with only one subrange and splits it - example 2 */
  2340.         j=xblast; i=(xblast+=2);
  2341.         while(j>=b)
  2342.             xbstem[i--]=xbstem[j--];
  2343.         xbstem[b]=from-1;
  2344.         xbstem[b+1]=to+1;
  2345.         return 1;
  2346.     /* becomes
  2347.      * 2a
  2348.      *     a|b   ||          |-----|    |-----|     |-----|
  2349.      *      f|--|t
  2350.      */
  2351.     }
  2352.  
  2353.     if(xbstem[b-1] < from) {
  2354.         /* cuts the back of this subrange - examples 3, 4, 7 */
  2355.         xbstem[b] = from-1;
  2356.         b+=2;
  2357.     /* becomes
  2358.      * 3a
  2359.      *     a|----|           |-----|b   |-----|     |-----|
  2360.      *           f|-----|t
  2361.      * 4a
  2362.      *      |---|           a|-----|b   |-----|     |-----|
  2363.      *          f|------------|t
  2364.      * 7a
  2365.      *      |---|            |-----|b  a|-----|     |-----|
  2366.      *          f|--------------------------|t
  2367.      */
  2368.     }
  2369.  
  2370.     if(xbstem[a+1] > to) {
  2371.         /* cuts the front of this subrange - examples 4a, 5, 7a */
  2372.         xbstem[a] = to+1;
  2373.         a-=2;
  2374.     /* becomes
  2375.      * 4b
  2376.      *     a|---|              |---|b   |-----|     |-----|
  2377.      *          f|------------|t
  2378.      * 5b
  2379.      *      |-----|          |-----|b  a|-----|          ||
  2380.      *                   f|-----------------------------|t
  2381.      * 7b
  2382.      *      |---|           a|-----|b        ||     |-----|
  2383.      *          f|--------------------------|t
  2384.      */
  2385.     }
  2386.  
  2387.     if(a < b-1) /* now after modification it hits a gap - examples 3a, 4b */
  2388.         return 1; /* because we have removed something */
  2389.  
  2390.     /* now remove the subranges completely covered by the new stem */
  2391.     /* examples 5b, 6, 7b */
  2392.     i=b-1; j=a+2;
  2393.     /* positioned as:
  2394.      * 5b                    i                           j
  2395.      *      |-----|          |-----|b  a|-----|          ||
  2396.      *                   f|-----------------------------|t
  2397.      * 6    i                                             xblast  j
  2398.      *      |-----|b         |-----|    |-----|    a|-----|
  2399.      *   f|--------------------------------------------------|t
  2400.      * 7b                    i               j
  2401.      *      |---|           a|-----|b        ||     |-----|
  2402.      *          f|--------------------------|t
  2403.      */
  2404.     while(j <= xblast)
  2405.         xbstem[i++]=xbstem[j++];
  2406.     xblast=i-1;
  2407.     return 1;
  2408. }
  2409.  
  2410. /* for debugging */
  2411. static void
  2412. printbasestem(void)
  2413. {
  2414.     int i;
  2415.  
  2416.     printf("( ");
  2417.     for(i=0; i<xblast; i+=2)
  2418.         printf("%d-%d ", xbstem[i], xbstem[i+1]);
  2419.     printf(") %d\n", xblast);
  2420. }
  2421.  
  2422. /*
  2423.  * Join the stem borders to build the sets of substituted stems
  2424.  * XXX add consideration of the italic angle
  2425.  */
  2426. static void
  2427. joinsubstems(
  2428.       STEM * s,
  2429.       short *pairs,
  2430.       int nold,
  2431.       int useblues /* do we use the blue values ? */
  2432. )
  2433. {
  2434.     int i, j, x;
  2435.     static unsigned char mx[MAX_STEMS][MAX_STEMS];
  2436.  
  2437.     /* we do the substituted groups of stems first
  2438.      * and it looks like it's going to be REALLY SLOW 
  2439.      * AND PAINFUL but let's bother about it later
  2440.      */
  2441.  
  2442.     /* for the substituted stems we don't bother about [hv]stem3 -
  2443.      * anyway the X11R6 rasterizer does not bother about hstem3
  2444.      * at all and is able to handle only one global vstem3
  2445.      * per glyph 
  2446.      */
  2447.  
  2448.     /* clean the used part of matrix */
  2449.     for(i=0; i<nold; i++)
  2450.         for(j=0; j<nold; j++)
  2451.             mx[i][j]=0;
  2452.  
  2453.     /* build the matrix of stem pairs */
  2454.     for(i=0; i<nold; i++) {
  2455.         if( s[i].flags & ST_ZONE )
  2456.             continue;
  2457.         if(s[i].flags & ST_BLUE)
  2458.             mx[i][i]=1; /* allow to pair with itself if no better pair */
  2459.         if(s[i].flags & ST_UP) { /* the down-stems are already matched */
  2460.             setbasestem(s[i].from, s[i].to);
  2461.             for(j=i+1; j<nold; j++) {
  2462.                 if(s[i].value==s[j].value
  2463.                 || s[j].flags & ST_ZONE ) {
  2464.                     continue;
  2465.                 }
  2466.                 x=subfrombase(s[j].from, s[j].to);
  2467.  
  2468.                 if(s[j].flags & ST_UP) /* match only up+down pairs */
  2469.                     continue;
  2470.  
  2471.                 mx[i][j]=mx[j][i]=x;
  2472.  
  2473.                 if(isbaseempty()) /* nothing else to do */
  2474.                     break;
  2475.             }
  2476.         }
  2477.     }
  2478.  
  2479.     if(ISDBG(SUBSTEMS)) {
  2480.         fprintf(pfa_file, "%%     ");
  2481.         for(j=0; j<nold; j++)
  2482.             putc( j%10==0 ? '0'+(j/10)%10 : ' ', pfa_file);
  2483.         fprintf(pfa_file, "\n%%     ");
  2484.         for(j=0; j<nold; j++)
  2485.             putc('0'+j%10, pfa_file);
  2486.         putc('\n', pfa_file);
  2487.         for(i=0; i<nold; i++) {
  2488.             fprintf(pfa_file, "%% %3d ",i);
  2489.             for(j=0; j<nold; j++)
  2490.                 putc( mx[i][j] ? 'X' : '.', pfa_file);
  2491.             putc('\n', pfa_file);
  2492.         }
  2493.     }
  2494.  
  2495.     /* now use the matrix to find the best pair for each stem */
  2496.     for(i=0; i<nold; i++) {
  2497.         int pri, lastpri, v, f;
  2498.  
  2499.         x= -1; /* best pair: none */
  2500.         lastpri=0;
  2501.  
  2502.         v=s[i].value;
  2503.         f=s[i].flags;
  2504.  
  2505.         if(f & ST_ZONE) {
  2506.             pairs[i]= -1;
  2507.             continue;
  2508.         }
  2509.  
  2510.         if(f & ST_UP) {
  2511.             for(j=i+1; j<nold; j++) {
  2512.                 if(mx[i][j]==0)
  2513.                     continue;
  2514.  
  2515.                 if( (f | s[j].flags) & ST_END )
  2516.                     pri=1;
  2517.                 else if( (f | s[j].flags) & ST_FLAT )
  2518.                     pri=3;
  2519.                 else
  2520.                     pri=2;
  2521.  
  2522.                 if(lastpri==0
  2523.                 || pri > lastpri  
  2524.                 && ( lastpri==1 || s[j].value-v<20 || (s[x].value-v)*2 >= s[j].value-v ) ) {
  2525.                     lastpri=pri;
  2526.                     x=j;
  2527.                 }
  2528.             }
  2529.         } else {
  2530.             for(j=i-1; j>=0; j--) {
  2531.                 if(mx[i][j]==0)
  2532.                     continue;
  2533.  
  2534.                 if( (f | s[j].flags) & ST_END )
  2535.                     pri=1;
  2536.                 else if( (f | s[j].flags) & ST_FLAT )
  2537.                     pri=3;
  2538.                 else
  2539.                     pri=2;
  2540.  
  2541.                 if(lastpri==0
  2542.                 || pri > lastpri  
  2543.                 && ( lastpri==1 || v-s[j].value<20 || (v-s[x].value)*2 >= v-s[j].value ) ) {
  2544.                     lastpri=pri;
  2545.                     x=j;
  2546.                 }
  2547.             }
  2548.         }
  2549.         if(x== -1 && mx[i][i])
  2550.             pairs[i]=i; /* a special case */
  2551.         else
  2552.             pairs[i]=x;
  2553.     }
  2554.  
  2555.     if(ISDBG(SUBSTEMS)) {
  2556.         for(i=0; i<nold; i++) {
  2557.             j=pairs[i];
  2558.             if(j>0)
  2559.                 fprintf(pfa_file, "%% %d...%d  (%d x %d)\n", s[i].value, s[j].value, i, j);
  2560.         }
  2561.     }
  2562. }
  2563.  
  2564. /*
  2565.  * Make all the stems originating at the same value get the
  2566.  * same width. Without this the rasterizer may move the dots
  2567.  * randomly up or down by one pixel, and that looks bad.
  2568.  * The prioritisation is the same as in findstemat().
  2569.  */
  2570. static void
  2571. uniformstems(
  2572.       STEM * s,
  2573.       short *pairs,
  2574.       int ns
  2575. )
  2576. {
  2577.     int i, j, from, to, val, dir;
  2578.     int pri, prevpri[2], wd, prevwd[2], prevbest[2];
  2579.  
  2580.     for(from=0; from<ns; from=to) {
  2581.         prevpri[0] = prevpri[1] = 0;
  2582.         prevwd[0] = prevwd[1] = 0;
  2583.         prevbest[0] = prevbest[1] = -1;
  2584.         val = s[from].value;
  2585.  
  2586.         for(to = from; to<ns && s[to].value == val; to++) {
  2587.             dir = ((s[to].flags & ST_UP)!=0);
  2588.  
  2589.             i=pairs[to]; /* the other side of this stem */
  2590.             if(i<0 || i==to)
  2591.                 continue; /* oops, no other side */
  2592.             wd=abs(s[i].value-val);
  2593.             if(wd == 0)
  2594.                 continue;
  2595.             pri=1;
  2596.             if( (s[to].flags | s[i].flags) & ST_END )
  2597.                 pri=0;
  2598.             if( prevbest[dir] == -1 || pri > prevpri[dir] || wd<prevwd[dir] ) {
  2599.                 prevbest[dir]=i;
  2600.                 prevpri[dir]=pri;
  2601.                 prevwd[dir]=wd;
  2602.             }
  2603.         }
  2604.  
  2605.         for(i=from; i<to; i++) {
  2606.             dir = ((s[i].flags & ST_UP)!=0);
  2607.             if(prevbest[dir] >= 0) {
  2608.                 if(ISDBG(SUBSTEMS)) {
  2609.                     fprintf(stderr, "at %d (%s %d) pair %d->%d(%d)\n", i, 
  2610.                         (dir ? "UP":"DOWN"), s[i].value, pairs[i], prevbest[dir],
  2611.                         s[prevbest[dir]].value);
  2612.                 }
  2613.                 pairs[i] = prevbest[dir];
  2614.             }
  2615.         }
  2616.     }
  2617. }
  2618.  
  2619. /* 
  2620.  * Find the best stem in the array at the specified (value, origin),
  2621.  * related to the entry ge.
  2622.  * Returns its index in the array sp, -1 means "none".
  2623.  * prevbest is the result for the other end of the line, we must 
  2624.  * find something better than it or leave it as it is.
  2625.  */
  2626. static int
  2627. findstemat(
  2628.     int value,
  2629.     int origin,
  2630.     GENTRY *ge,
  2631.     STEM *sp,
  2632.     short *pairs,
  2633.     int ns,
  2634.     int prevbest /* -1 means "none" */
  2635. )
  2636. {
  2637.     int i, min, max;
  2638.     int v, si;
  2639.     int pri, prevpri; /* priority, 0 = has ST_END, 1 = no ST_END */
  2640.     int wd, prevwd; /* stem width */
  2641.  
  2642.     si= -1; /* nothing yet */
  2643.  
  2644.     /* stems are ordered by value, binary search */
  2645.     min=0; max=ns; /* min <= i < max */
  2646.     while( min < max ) {
  2647.         i=(min+max)/2;
  2648.         v=sp[i].value;
  2649.         if(v<value)
  2650.             min=i+1;
  2651.         else if(v>value)
  2652.             max=i;
  2653.         else {
  2654.             si=i; /* temporary value */
  2655.             break;
  2656.         }
  2657.     }
  2658.  
  2659.     if( si < 0 ) /* found nothing this time */
  2660.         return prevbest;
  2661.  
  2662.     /* find the priority of the prevbest */
  2663.     /* we expect that prevbest has a pair */
  2664.     if(prevbest>=0) {
  2665.         i=pairs[prevbest];
  2666.         prevpri=1;
  2667.         if( (sp[prevbest].flags | sp[i].flags) & ST_END )
  2668.             prevpri=0; 
  2669.         prevwd=abs(sp[i].value-value);
  2670.     }
  2671.  
  2672.     /* stems are not ordered by origin, so now do the linear search */
  2673.  
  2674.     while( si>0 && sp[si-1].value==value ) /* find the first one */
  2675.         si--;
  2676.  
  2677.     for(; si<ns && sp[si].value==value; si++) {
  2678.         if(sp[si].origin != origin) 
  2679.             continue;
  2680.         if(sp[si].ge != ge) {
  2681.             if(ISDBG(SUBSTEMS)) {
  2682.                 fprintf(stderr, 
  2683.                     "dbg: possible self-intersection at v=%d o=%d exp_ge=0x%x ge=0x%x\n",
  2684.                     value, origin, ge, sp[si].ge);
  2685.             }
  2686.             continue;
  2687.         }
  2688.         i=pairs[si]; /* the other side of this stem */
  2689.         if(i<0)
  2690.             continue; /* oops, no other side */
  2691.         pri=1;
  2692.         if( (sp[si].flags | sp[i].flags) & ST_END )
  2693.             pri=0;
  2694.         wd=abs(sp[i].value-value);
  2695.         if( prevbest == -1 || pri >prevpri 
  2696.         || pri==prevpri && prevwd==0 || wd!=0 && wd<prevwd ) {
  2697.             prevbest=si;
  2698.             prevpri=pri;
  2699.             prevwd=wd;
  2700.             continue;
  2701.         }
  2702.     }
  2703.  
  2704.     return prevbest;
  2705. }
  2706.  
  2707. /* add the substems for one glyph entry 
  2708.  * (called from groupsubstems())
  2709.  * returns 0 if all OK, 1 if too many groups
  2710.  */
  2711.  
  2712. static int gssentry_lastgrp=0; /* reset to 0 for each new glyph */
  2713.  
  2714. static int
  2715. gssentry( /* crazy number of parameters */
  2716.     GENTRY *ge,
  2717.     STEM *hs, /* horizontal stems, sorted by value */
  2718.     short *hpairs,
  2719.     int nhs,
  2720.     STEM *vs, /* vertical stems, sorted by value */
  2721.     short *vpairs,
  2722.     int nvs,
  2723.     STEMBOUNDS *s,
  2724.     short *egp,
  2725.     int *nextvsi, 
  2726.     int *nexthsi /* -2 means "check by yourself" */
  2727. ) {
  2728.     enum {
  2729.         SI_VP,    /* vertical primary */
  2730.         SI_HP,    /* horizontal primary */
  2731.         SI_SIZE /* size of the array */
  2732.     };
  2733.     int si[SI_SIZE]; /* indexes of relevant stems */
  2734.  
  2735.     /* the bounds of the existing relevant stems */
  2736.     STEMBOUNDS r[ sizeof(si) / sizeof(si[0]) * 2 ];
  2737.     char rexpand; /* by how much we need to expand the group */
  2738.     int nr; /* and the number of them */
  2739.  
  2740.     /* yet more temporary storage */
  2741.     short lb, hb, isvert;
  2742.     int conflict, grp;
  2743.     int i, j, x, y;
  2744.  
  2745.  
  2746.     /* for each line or curve we try to find a horizontal and
  2747.      * a vertical stem corresponding to its first point
  2748.      * (corresponding to the last point of the previous
  2749.      * glyph entry), because the directions of the lines
  2750.      * will be eventually reversed and it will then become the last
  2751.      * point. And the T1 rasterizer applies the hints to 
  2752.      * the last point.
  2753.      *
  2754.      */
  2755.  
  2756.     /* start with the common part, the first point */
  2757.     x=ge->prev->ix3;
  2758.     y=ge->prev->iy3;
  2759.  
  2760.     if(*nextvsi == -2)
  2761.         si[SI_VP]=findstemat(x, y, ge, vs, vpairs, nvs, -1);
  2762.     else {
  2763.         si[SI_VP]= *nextvsi; *nextvsi= -2;
  2764.     }
  2765.     if(*nexthsi == -2)
  2766.         si[SI_HP]=findstemat(y, x, ge, hs, hpairs, nhs, -1);
  2767.     else {
  2768.         si[SI_HP]= *nexthsi; *nexthsi= -2;
  2769.     }
  2770.  
  2771.     /*
  2772.      * For the horizontal lines we make sure that both
  2773.      * ends of the line have the same horizontal stem,
  2774.      * and the same thing for vertical lines and stems.
  2775.      * In both cases we enforce the stem for the next entry.
  2776.      * Otherwise unpleasant effects may arise.
  2777.      */
  2778.  
  2779.     if(ge->type==GE_LINE) {
  2780.         if(ge->ix3==x) { /* vertical line */
  2781.             *nextvsi=si[SI_VP]=findstemat(x, ge->iy3, ge->frwd, vs, vpairs, nvs, si[SI_VP]);
  2782.         } else if(ge->iy3==y) { /* horizontal line */
  2783.             *nexthsi=si[SI_HP]=findstemat(y, ge->ix3, ge->frwd, hs, hpairs, nhs, si[SI_HP]);
  2784.         }
  2785.     }
  2786.  
  2787.     if(si[SI_VP]+si[SI_HP] == -2) /* no stems, leave it alone */
  2788.         return 0;
  2789.  
  2790.     /* build the array of relevant bounds */
  2791.     nr=0;
  2792.     for(i=0; i< sizeof(si) / sizeof(si[0]); i++) {
  2793.         STEM *sp;
  2794.         short *pairs;
  2795.         int step;
  2796.         int f;
  2797.         int nzones, firstzone, binzone, einzone;
  2798.         int btype, etype;
  2799.  
  2800.         if(si[i] < 0)
  2801.             continue;
  2802.  
  2803.         if(i<SI_HP) {
  2804.             r[nr].isvert=1; sp=vs; pairs=vpairs;
  2805.         } else {
  2806.             r[nr].isvert=0; sp=hs; pairs=hpairs;
  2807.         }
  2808.  
  2809.         r[nr].low=sp[ si[i] ].value;
  2810.         r[nr].high=sp[ pairs[ si[i] ] ].value;
  2811.  
  2812.         if(r[nr].low > r[nr].high) {
  2813.             j=r[nr].low; r[nr].low=r[nr].high; r[nr].high=j;
  2814.             step= -1;
  2815.         } else {
  2816.             step=1;
  2817.         }
  2818.  
  2819.         /* handle the interaction with Blue Zones */
  2820.  
  2821.         if(i>=SI_HP) { /* only for horizontal stems */
  2822.             if(si[i]==pairs[si[i]]) {
  2823.                 /* special case, the outermost stem in the
  2824.                  * Blue Zone without a pair, simulate it to 20-pixel
  2825.                  */
  2826.                 if(sp[ si[i] ].flags & ST_UP) {
  2827.                     r[nr].high+=20;
  2828.                     for(j=si[i]+1; j<nhs; j++)
  2829.                         if( (sp[j].flags & (ST_ZONE|ST_TOPZONE))
  2830.                         == (ST_ZONE|ST_TOPZONE) ) {
  2831.                             if(r[nr].high > sp[j].value-2)
  2832.                                 r[nr].high=sp[j].value-2;
  2833.                             break;
  2834.                         }
  2835.                 } else {
  2836.                     r[nr].low-=20;
  2837.                     for(j=si[i]-1; j>=0; j--)
  2838.                         if( (sp[j].flags & (ST_ZONE|ST_TOPZONE))
  2839.                         == (ST_ZONE) ) {
  2840.                             if(r[nr].low < sp[j].value+2)
  2841.                                 r[nr].low=sp[j].value+2;
  2842.                             break;
  2843.                         }
  2844.                 }
  2845.             }
  2846.  
  2847.             /* check that the stem borders don't end up in
  2848.              * different Blue Zones */
  2849.             f=sp[ si[i] ].flags;
  2850.             nzones=0; einzone=binzone=0;
  2851.             for(j=si[i]; j!=pairs[ si[i] ]; j+=step) {
  2852.                 if( (sp[j].flags & ST_ZONE)==0 )
  2853.                     continue;
  2854.                 /* if see a zone border going in the same direction */
  2855.                 if( ((f ^ sp[j].flags) & ST_UP)==0 ) {
  2856.                     if( ++nzones == 1 ) {
  2857.                         firstzone=sp[j].value; /* remember the first one */
  2858.                         etype=sp[j].flags & ST_TOPZONE;
  2859.                     }
  2860.                     einzone=1;
  2861.  
  2862.                 } else { /* the opposite direction */
  2863.                     if(nzones==0) { /* beginning is in a blue zone */
  2864.                         binzone=1;
  2865.                         btype=sp[j].flags & ST_TOPZONE;
  2866.                     }
  2867.                     einzone=0;
  2868.                 }
  2869.             }
  2870.  
  2871.             /* beginning and end are in Blue Zones of different types */
  2872.             if( binzone && einzone && (btype ^ etype)!=0 ) {
  2873.                 if( sp[si[i]].flags & ST_UP ) {
  2874.                     if(firstzone > r[nr].low+22)
  2875.                         r[nr].high=r[nr].low+20;
  2876.                     else
  2877.                         r[nr].high=firstzone-2;
  2878.                 } else {
  2879.                     if(firstzone < r[nr].high-22)
  2880.                         r[nr].low=r[nr].high-20;
  2881.                     else
  2882.                         r[nr].low=firstzone+2;
  2883.                 }
  2884.             }
  2885.         }
  2886.  
  2887.         if(ISDBG(SUBSTEMS))
  2888.             fprintf(pfa_file, "%%  at(%d,%d)[%d,%d] %d..%d %c (%d x %d)\n", x, y, i, nr,
  2889.                 r[nr].low, r[nr].high, r[nr].isvert ? 'v' : 'h',
  2890.                 si[i], pairs[si[i]]);
  2891.  
  2892.         nr++;
  2893.     }
  2894.  
  2895.     /* now try to find a group */
  2896.     conflict=0; /* no conflicts found yet */
  2897.     for(j=0; j<nr; j++)
  2898.         r[j].already=0;
  2899.  
  2900.     /* check if it fits into the last group */
  2901.     grp = gssentry_lastgrp;
  2902.     i = (grp==0)? 0 : egp[grp-1];
  2903.     for(; i<egp[grp]; i++) {
  2904.         lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
  2905.         for(j=0; j<nr; j++)
  2906.             if( r[j].isvert==isvert  /* intersects */
  2907.             && r[j].low <= hb && r[j].high >= lb ) {
  2908.                 if( r[j].low == lb && r[j].high == hb ) /* coincides */
  2909.                     r[j].already=1;
  2910.                 else
  2911.                     conflict=1;
  2912.             }
  2913.  
  2914.         if(conflict) 
  2915.             break;
  2916.     }
  2917.  
  2918.     if(conflict) { /* nope, check all the groups */
  2919.         for(j=0; j<nr; j++)
  2920.             r[j].already=0;
  2921.  
  2922.         for(i=0, grp=0; i<egp[NSTEMGRP-1]; i++) {
  2923.             if(i == egp[grp]) { /* checked all stems in a group */
  2924.                 if(conflict) {
  2925.                     grp++; conflict=0; /* check the next group */
  2926.                     for(j=0; j<nr; j++)
  2927.                         r[j].already=0;
  2928.                 } else
  2929.                     break; /* insert into this group */
  2930.             }
  2931.  
  2932.             lb=s[i].low; hb=s[i].high; isvert=s[i].isvert;
  2933.             for(j=0; j<nr; j++)
  2934.                 if( r[j].isvert==isvert  /* intersects */
  2935.                 && r[j].low <= hb && r[j].high >= lb ) {
  2936.                     if( r[j].low == lb && r[j].high == hb ) /* coincides */
  2937.                         r[j].already=1;
  2938.                     else
  2939.                         conflict=1;
  2940.                 }
  2941.  
  2942.             if(conflict) 
  2943.                 i=egp[grp]-1; /* fast forward to the next group */
  2944.         }
  2945.     }
  2946.  
  2947.     /* do we have any empty group ? */
  2948.     if(conflict && grp < NSTEMGRP-1) {
  2949.         grp++; conflict=0;
  2950.         for(j=0; j<nr; j++)
  2951.             r[j].already=0;
  2952.     }
  2953.  
  2954.     if(conflict) { /* oops, can't find any group to fit */
  2955.         return 1;
  2956.     }
  2957.  
  2958.     /* OK, add stems to this group */
  2959.  
  2960.     rexpand = nr;
  2961.     for(j=0; j<nr; j++)
  2962.         rexpand -= r[j].already;
  2963.  
  2964.     if(rexpand > 0) {
  2965.         for(i=egp[NSTEMGRP-1]-1; i>=egp[grp]; i--)
  2966.             s[i+rexpand]=s[i];
  2967.         for(i=0; i<nr; i++)
  2968.             if(!r[i].already)
  2969.                 s[egp[grp]++]=r[i];
  2970.         for(i=grp+1; i<NSTEMGRP; i++)
  2971.             egp[i]+=rexpand;
  2972.     }
  2973.  
  2974.     ge->stemid = gssentry_lastgrp = grp;
  2975.     return 0;
  2976. }
  2977.  
  2978. /*
  2979.  * Create the groups of substituted stems from the list.
  2980.  * Each group will be represented by a subroutine in the Subs
  2981.  * array.
  2982.  */
  2983.  
  2984. static void
  2985. groupsubstems(
  2986.     GLYPH *g,
  2987.     STEM *hs, /* horizontal stems, sorted by value */
  2988.     short *hpairs,
  2989.     int nhs,
  2990.     STEM *vs, /* vertical stems, sorted by value */
  2991.     short *vpairs,
  2992.     int nvs
  2993. )
  2994. {
  2995.     GENTRY *ge;
  2996.     int i, j;
  2997.  
  2998.     /* temporary storage */
  2999.     STEMBOUNDS s[MAX_STEMS*2];
  3000.     /* indexes in there, pointing past the end each stem group */
  3001.     short egp[NSTEMGRP]; 
  3002.  
  3003.     int nextvsi, nexthsi; /* -2 means "check by yourself" */
  3004.  
  3005.     for(i=0; i<NSTEMGRP; i++)
  3006.         egp[i]=0;
  3007.  
  3008.     nextvsi=nexthsi= -2; /* processed no horiz/vert line */
  3009.  
  3010.     gssentry_lastgrp = 0; /* reset the last group for new glyph */
  3011.  
  3012.     for (ge = g->entries; ge != 0; ge = ge->next) {
  3013.         if(ge->type!=GE_LINE && ge->type!=GE_CURVE) {
  3014.             nextvsi=nexthsi= -2; /* next path is independent */
  3015.             continue;
  3016.         }
  3017.  
  3018.         if( gssentry(ge, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) {
  3019.             WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n",
  3020.                 g->name, NSTEMGRP);
  3021.             /* it's better to have no substituted hints at all than have only part */
  3022.             for (ge = g->entries; ge != 0; ge = ge->next)
  3023.                 ge->stemid= -1;
  3024.             g->nsg=0; /* just to be safe, already is 0 by initialization */
  3025.             return;
  3026.         }
  3027.  
  3028.         /*
  3029.          * handle the last vert/horiz line of the path specially,
  3030.          * correct the hint for the first entry of the path
  3031.          */
  3032.         if(ge->frwd != ge->next && (nextvsi != -2 || nexthsi != -2) ) {
  3033.             if( gssentry(ge->frwd, hs, hpairs, nhs, vs, vpairs, nvs, s, egp, &nextvsi, &nexthsi) ) {
  3034.                 WARNING_2 fprintf(stderr, "*** glyph %s requires over %d hint subroutines, ignored them\n",
  3035.                     g->name, NSTEMGRP);
  3036.                 /* it's better to have no substituted hints at all than have only part */
  3037.                 for (ge = g->entries; ge != 0; ge = ge->next)
  3038.                     ge->stemid= -1;
  3039.                 g->nsg=0; /* just to be safe, already is 0 by initialization */
  3040.                 return;
  3041.             }
  3042.         }
  3043.  
  3044.     }
  3045.  
  3046.     /* find the index of the first empty group - same as the number of groups */
  3047.     if(egp[0]>0) {
  3048.         for(i=1; i<NSTEMGRP && egp[i]!=egp[i-1]; i++)
  3049.             {}
  3050.         g->nsg=i;
  3051.     } else
  3052.         g->nsg=0;
  3053.  
  3054.     if(ISDBG(SUBSTEMS)) {
  3055.         fprintf(pfa_file, "%% %d substem groups (%d %d %d)\n", g->nsg,
  3056.             g->nsg>1 ? egp[g->nsg-2] : -1,
  3057.             g->nsg>0 ? egp[g->nsg-1] : -1,
  3058.             g->nsg<NSTEMGRP ? egp[g->nsg] : -1 );
  3059.         j=0;
  3060.         for(i=0; i<g->nsg; i++) {
  3061.             fprintf(pfa_file, "%% grp %3d:      ", i);
  3062.             for(; j<egp[i]; j++) {
  3063.                 fprintf(pfa_file, " %4d...%-4d %c  ", s[j].low, s[j].high,
  3064.                     s[j].isvert ? 'v' : 'h');
  3065.             }
  3066.             fprintf(pfa_file, "\n");
  3067.         }
  3068.     }
  3069.  
  3070.     if(g->nsg==1) { /* it would be the same as the main stems */
  3071.         /* so erase it */
  3072.         for (ge = g->entries; ge != 0; ge = ge->next)
  3073.             ge->stemid= -1;
  3074.         g->nsg=0;
  3075.     }
  3076.  
  3077.     if(g->nsg>0) {
  3078.         if( (g->nsbs=malloc(g->nsg * sizeof (egp[0]))) == 0 ) {
  3079.             fprintf(stderr, "**** not enough memory for substituted hints ****\n");
  3080.             exit(255);
  3081.         }
  3082.         memmove(g->nsbs, egp, g->nsg * sizeof(short));
  3083.         if( (g->sbstems=malloc(egp[g->nsg-1] * sizeof (s[0]))) == 0 ) {
  3084.             fprintf(stderr, "**** not enough memory for substituted hints ****\n");
  3085.             exit(255);
  3086.         }
  3087.         memmove(g->sbstems, s, egp[g->nsg-1] * sizeof(s[0]));
  3088.     }
  3089. }
  3090.  
  3091. void
  3092. buildstems(
  3093.        GLYPH * g
  3094. )
  3095. {
  3096.     STEM            hs[MAX_STEMS], vs[MAX_STEMS];    /* temporary working
  3097.                              * storage */
  3098.     short    hs_pairs[MAX_STEMS], vs_pairs[MAX_STEMS]; /* best pairs for these stems */
  3099.     STEM           *sp;
  3100.     GENTRY         *ge, *nge, *pge;
  3101.     int             nx, ny;
  3102.     int ovalue;
  3103.     int totals, grp, lastgrp;
  3104.  
  3105.     assertisint(g, "buildstems int");
  3106.  
  3107.     g->nhs = g->nvs = 0;
  3108.     memset(hs, 0, sizeof hs);
  3109.     memset(vs, 0, sizeof vs);
  3110.  
  3111.     /* first search the whole character for possible stem points */
  3112.  
  3113.     for (ge = g->entries; ge != 0; ge = ge->next) {
  3114.         if (ge->type == GE_CURVE) {
  3115.  
  3116.             /*
  3117.              * SURPRISE! 
  3118.              * We consider the stems bound by the
  3119.              * H/V ends of the curves as flat ones.
  3120.              *
  3121.              * But we don't include the point on the
  3122.              * other end into the range.
  3123.              */
  3124.  
  3125.             /* first check the beginning of curve */
  3126.             /* if it is horizontal, add a hstem */
  3127.             if (ge->iy1 == ge->prev->iy3) {
  3128.                 hs[g->nhs].value = ge->iy1;
  3129.  
  3130.                 if (ge->ix1 < ge->prev->ix3)
  3131.                     hs[g->nhs].flags = ST_FLAT | ST_UP;
  3132.                 else
  3133.                     hs[g->nhs].flags = ST_FLAT;
  3134.  
  3135.                 hs[g->nhs].origin = ge->prev->ix3;
  3136.                 hs[g->nhs].ge = ge;
  3137.  
  3138.                 if (ge->ix1 < ge->prev->ix3) {
  3139.                     hs[g->nhs].from = ge->ix1+1;
  3140.                     hs[g->nhs].to = ge->prev->ix3;
  3141.                     if(hs[g->nhs].from > hs[g->nhs].to)
  3142.                         hs[g->nhs].from--;
  3143.                 } else {
  3144.                     hs[g->nhs].from = ge->prev->ix3;
  3145.                     hs[g->nhs].to = ge->ix1-1;
  3146.                     if(hs[g->nhs].from > hs[g->nhs].to)
  3147.                         hs[g->nhs].to++;
  3148.                 }
  3149.                 if (ge->ix1 != ge->prev->ix3)
  3150.                     g->nhs++;
  3151.             }
  3152.             /* if it is vertical, add a vstem */
  3153.             else if (ge->ix1 == ge->prev->ix3) {
  3154.                 vs[g->nvs].value = ge->ix1;
  3155.  
  3156.                 if (ge->iy1 > ge->prev->iy3)
  3157.                     vs[g->nvs].flags = ST_FLAT | ST_UP;
  3158.                 else
  3159.                     vs[g->nvs].flags = ST_FLAT;
  3160.  
  3161.                 vs[g->nvs].origin = ge->prev->iy3;
  3162.                 vs[g->nvs].ge = ge;
  3163.  
  3164.                 if (ge->iy1 < ge->prev->iy3) {
  3165.                     vs[g->nvs].from = ge->iy1+1;
  3166.                     vs[g->nvs].to = ge->prev->iy3;
  3167.                     if(vs[g->nvs].from > vs[g->nvs].to)
  3168.                         vs[g->nvs].from--;
  3169.                 } else {
  3170.                     vs[g->nvs].from = ge->prev->iy3;
  3171.                     vs[g->nvs].to = ge->iy1-1;
  3172.                     if(vs[g->nvs].from > vs[g->nvs].to)
  3173.                         vs[g->nvs].to++;
  3174.                 }
  3175.  
  3176.                 if (ge->iy1 != ge->prev->iy3)
  3177.                     g->nvs++;
  3178.             }
  3179.             /* then check the end of curve */
  3180.             /* if it is horizontal, add a hstem */
  3181.             if (ge->iy3 == ge->iy2) {
  3182.                 hs[g->nhs].value = ge->iy3;
  3183.  
  3184.                 if (ge->ix3 < ge->ix2)
  3185.                     hs[g->nhs].flags = ST_FLAT | ST_UP;
  3186.                 else
  3187.                     hs[g->nhs].flags = ST_FLAT;
  3188.  
  3189.                 hs[g->nhs].origin = ge->ix3;
  3190.                 hs[g->nhs].ge = ge->frwd;
  3191.  
  3192.                 if (ge->ix3 < ge->ix2) {
  3193.                     hs[g->nhs].from = ge->ix3;
  3194.                     hs[g->nhs].to = ge->ix2-1;
  3195.                     if( hs[g->nhs].from > hs[g->nhs].to )
  3196.                         hs[g->nhs].to++;
  3197.                 } else {
  3198.                     hs[g->nhs].from = ge->ix2+1;
  3199.                     hs[g->nhs].to = ge->ix3;
  3200.                     if( hs[g->nhs].from > hs[g->nhs].to )
  3201.                         hs[g->nhs].from--;
  3202.                 }
  3203.  
  3204.                 if (ge->ix3 != ge->ix2)
  3205.                     g->nhs++;
  3206.             }
  3207.             /* if it is vertical, add a vstem */
  3208.             else if (ge->ix3 == ge->ix2) {
  3209.                 vs[g->nvs].value = ge->ix3;
  3210.  
  3211.                 if (ge->iy3 > ge->iy2)
  3212.                     vs[g->nvs].flags = ST_FLAT | ST_UP;
  3213.                 else
  3214.                     vs[g->nvs].flags = ST_FLAT;
  3215.  
  3216.                 vs[g->nvs].origin = ge->iy3;
  3217.                 vs[g->nvs].ge = ge->frwd;
  3218.  
  3219.                 if (ge->iy3 < ge->iy2) {
  3220.                     vs[g->nvs].from = ge->iy3;
  3221.                     vs[g->nvs].to = ge->iy2-1;
  3222.                     if( vs[g->nvs].from > vs[g->nvs].to )
  3223.                         vs[g->nvs].to++;
  3224.                 } else {
  3225.                     vs[g->nvs].from = ge->iy2+1;
  3226.                     vs[g->nvs].to = ge->iy3;
  3227.                     if( vs[g->nvs].from > vs[g->nvs].to )
  3228.                         vs[g->nvs].from--;
  3229.                 }
  3230.  
  3231.                 if (ge->iy3 != ge->iy2)
  3232.                     g->nvs++;
  3233.             } else {
  3234.  
  3235.                 /*
  3236.                  * check the end of curve for a not smooth
  3237.                  * local extremum
  3238.                  */
  3239.                 nge = ge->frwd;
  3240.  
  3241.                 if (nge == 0)
  3242.                     continue;
  3243.                 else if (nge->type == GE_LINE) {
  3244.                     nx = nge->ix3;
  3245.                     ny = nge->iy3;
  3246.                 } else if (nge->type == GE_CURVE) {
  3247.                     nx = nge->ix1;
  3248.                     ny = nge->iy1;
  3249.                 } else
  3250.                     continue;
  3251.  
  3252.                 /* check for vertical extremums */
  3253.                 if (ge->iy3 > ge->iy2 && ge->iy3 > ny
  3254.                 || ge->iy3 < ge->iy2 && ge->iy3 < ny) {
  3255.                     hs[g->nhs].value = ge->iy3;
  3256.                     hs[g->nhs].from
  3257.                         = hs[g->nhs].to
  3258.                         = hs[g->nhs].origin = ge->ix3;
  3259.                     hs[g->nhs].ge = ge->frwd;
  3260.  
  3261.                     if (ge->ix3 < ge->ix2
  3262.                         || nx < ge->ix3)
  3263.                         hs[g->nhs].flags = ST_UP;
  3264.                     else
  3265.                         hs[g->nhs].flags = 0;
  3266.  
  3267.                     if (ge->ix3 != ge->ix2 || nx != ge->ix3)
  3268.                         g->nhs++;
  3269.                 }
  3270.                 /*
  3271.                  * the same point may be both horizontal and
  3272.                  * vertical extremum
  3273.                  */
  3274.                 /* check for horizontal extremums */
  3275.                 if (ge->ix3 > ge->ix2 && ge->ix3 > nx
  3276.                 || ge->ix3 < ge->ix2 && ge->ix3 < nx) {
  3277.                     vs[g->nvs].value = ge->ix3;
  3278.                     vs[g->nvs].from
  3279.                         = vs[g->nvs].to
  3280.                         = vs[g->nvs].origin = ge->iy3;
  3281.                     vs[g->nvs].ge = ge->frwd;
  3282.  
  3283.                     if (ge->iy3 > ge->iy2
  3284.                         || ny > ge->iy3)
  3285.                         vs[g->nvs].flags = ST_UP;
  3286.                     else
  3287.                         vs[g->nvs].flags = 0;
  3288.  
  3289.                     if (ge->iy3 != ge->iy2 || ny != ge->iy3)
  3290.                         g->nvs++;
  3291.                 }
  3292.             }
  3293.  
  3294.         } else if (ge->type == GE_LINE) {
  3295.             nge = ge->frwd;
  3296.  
  3297.             /* if it is horizontal, add a hstem */
  3298.             /* and the ends as vstems if they brace the line */
  3299.             if (ge->iy3 == ge->prev->iy3
  3300.             && ge->ix3 != ge->prev->ix3) {
  3301.                 hs[g->nhs].value = ge->iy3;
  3302.                 if (ge->ix3 < ge->prev->ix3) {
  3303.                     hs[g->nhs].flags = ST_FLAT | ST_UP;
  3304.                     hs[g->nhs].from = ge->ix3;
  3305.                     hs[g->nhs].to = ge->prev->ix3;
  3306.                 } else {
  3307.                     hs[g->nhs].flags = ST_FLAT;
  3308.                     hs[g->nhs].from = ge->prev->ix3;
  3309.                     hs[g->nhs].to = ge->ix3;
  3310.                 }
  3311.                 hs[g->nhs].origin = ge->ix3;
  3312.                 hs[g->nhs].ge = ge->frwd;
  3313.  
  3314.                 pge = ge->bkwd;
  3315.  
  3316.                 /* add beginning as vstem */
  3317.                 vs[g->nvs].value = pge->ix3;
  3318.                 vs[g->nvs].origin
  3319.                     = vs[g->nvs].from
  3320.                     = vs[g->nvs].to = pge->iy3;
  3321.                 vs[g->nvs].ge = ge;
  3322.  
  3323.                 if(pge->type==GE_CURVE)
  3324.                     ovalue=pge->iy2;
  3325.                 else
  3326.                     ovalue=pge->prev->iy3;
  3327.  
  3328.                 if (pge->iy3 > ovalue)
  3329.                     vs[g->nvs].flags = ST_UP | ST_END;
  3330.                 else if (pge->iy3 < ovalue)
  3331.                     vs[g->nvs].flags = ST_END;
  3332.                 else
  3333.                     vs[g->nvs].flags = 0;
  3334.  
  3335.                 if( vs[g->nvs].flags != 0 )
  3336.                     g->nvs++;
  3337.  
  3338.                 /* add end as vstem */
  3339.                 vs[g->nvs].value = ge->ix3;
  3340.                 vs[g->nvs].origin
  3341.                     = vs[g->nvs].from
  3342.                     = vs[g->nvs].to = ge->iy3;
  3343.                 vs[g->nvs].ge = ge->frwd;
  3344.  
  3345.                 if(nge->type==GE_CURVE)
  3346.                     ovalue=nge->iy1;
  3347.                 else
  3348.                     ovalue=nge->iy3;
  3349.  
  3350.                 if (ovalue > ge->iy3)
  3351.                     vs[g->nvs].flags = ST_UP | ST_END;
  3352.                 else if (ovalue < ge->iy3)
  3353.                     vs[g->nvs].flags = ST_END;
  3354.                 else
  3355.                     vs[g->nvs].flags = 0;
  3356.  
  3357.                 if( vs[g->nvs].flags != 0 )
  3358.                     g->nvs++;
  3359.  
  3360.                 g->nhs++;
  3361.             }
  3362.             /* if it is vertical, add a vstem */
  3363.             /* and the ends as hstems if they brace the line  */
  3364.             else if (ge->ix3 == ge->prev->ix3 
  3365.             && ge->iy3 != ge->prev->iy3) {
  3366.                 vs[g->nvs].value = ge->ix3;
  3367.                 if (ge->iy3 > ge->prev->iy3) {
  3368.                     vs[g->nvs].flags = ST_FLAT | ST_UP;
  3369.                     vs[g->nvs].from = ge->prev->iy3;
  3370.                     vs[g->nvs].to = ge->iy3;
  3371.                 } else {
  3372.                     vs[g->nvs].flags = ST_FLAT;
  3373.                     vs[g->nvs].from = ge->iy3;
  3374.                     vs[g->nvs].to = ge->prev->iy3;
  3375.                 }
  3376.                 vs[g->nvs].origin = ge->iy3;
  3377.                 vs[g->nvs].ge = ge->frwd;
  3378.  
  3379.                 pge = ge->bkwd;
  3380.  
  3381.                 /* add beginning as hstem */
  3382.                 hs[g->nhs].value = pge->iy3;
  3383.                 hs[g->nhs].origin
  3384.                     = hs[g->nhs].from
  3385.                     = hs[g->nhs].to = pge->ix3;
  3386.                 hs[g->nhs].ge = ge;
  3387.  
  3388.                 if(pge->type==GE_CURVE)
  3389.                     ovalue=pge->ix2;
  3390.                 else
  3391.                     ovalue=pge->prev->ix3;
  3392.  
  3393.                 if (pge->ix3 < ovalue)
  3394.                     hs[g->nhs].flags = ST_UP | ST_END;
  3395.                 else if (pge->ix3 > ovalue)
  3396.                     hs[g->nhs].flags = ST_END;
  3397.                 else
  3398.                     hs[g->nhs].flags = 0;
  3399.  
  3400.                 if( hs[g->nhs].flags != 0 )
  3401.                     g->nhs++;
  3402.  
  3403.                 /* add end as hstem */
  3404.                 hs[g->nhs].value = ge->iy3;
  3405.                 hs[g->nhs].origin
  3406.                     = hs[g->nhs].from
  3407.                     = hs[g->nhs].to = ge->ix3;
  3408.                 hs[g->nhs].ge = ge->frwd;
  3409.  
  3410.                 if(nge->type==GE_CURVE)
  3411.                     ovalue=nge->ix1;
  3412.                 else
  3413.                     ovalue=nge->ix3;
  3414.  
  3415.                 if (ovalue < ge->ix3)
  3416.                     hs[g->nhs].flags = ST_UP | ST_END;
  3417.                 else if (ovalue > ge->ix3)
  3418.                     hs[g->nhs].flags = ST_END;
  3419.                 else
  3420.                     hs[g->nhs].flags = 0;
  3421.  
  3422.                 if( hs[g->nhs].flags != 0 )
  3423.                     g->nhs++;
  3424.  
  3425.                 g->nvs++;
  3426.             }
  3427.             /*
  3428.              * check the end of line for a not smooth local
  3429.              * extremum
  3430.              */
  3431.             nge = ge->frwd;
  3432.  
  3433.             if (nge == 0)
  3434.                 continue;
  3435.             else if (nge->type == GE_LINE) {
  3436.                 nx = nge->ix3;
  3437.                 ny = nge->iy3;
  3438.             } else if (nge->type == GE_CURVE) {
  3439.                 nx = nge->ix1;
  3440.                 ny = nge->iy1;
  3441.             } else
  3442.                 continue;
  3443.  
  3444.             /* check for vertical extremums */
  3445.             if (ge->iy3 > ge->prev->iy3 && ge->iy3 > ny
  3446.             || ge->iy3 < ge->prev->iy3 && ge->iy3 < ny) {
  3447.                 hs[g->nhs].value = ge->iy3;
  3448.                 hs[g->nhs].from
  3449.                     = hs[g->nhs].to
  3450.                     = hs[g->nhs].origin = ge->ix3;
  3451.                 hs[g->nhs].ge = ge->frwd;
  3452.  
  3453.                 if (ge->ix3 < ge->prev->ix3
  3454.                     || nx < ge->ix3)
  3455.                     hs[g->nhs].flags = ST_UP;
  3456.                 else
  3457.                     hs[g->nhs].flags = 0;
  3458.  
  3459.                 if (ge->ix3 != ge->prev->ix3 || nx != ge->ix3)
  3460.                     g->nhs++;
  3461.             }
  3462.             /*
  3463.              * the same point may be both horizontal and vertical
  3464.              * extremum
  3465.              */
  3466.             /* check for horizontal extremums */
  3467.             if (ge->ix3 > ge->prev->ix3 && ge->ix3 > nx
  3468.             || ge->ix3 < ge->prev->ix3 && ge->ix3 < nx) {
  3469.                 vs[g->nvs].value = ge->ix3;
  3470.                 vs[g->nvs].from
  3471.                     = vs[g->nvs].to
  3472.                     = vs[g->nvs].origin = ge->iy3;
  3473.                 vs[g->nvs].ge = ge->frwd;
  3474.  
  3475.                 if (ge->iy3 > ge->prev->iy3
  3476.                     || ny > ge->iy3)
  3477.                     vs[g->nvs].flags = ST_UP;
  3478.                 else
  3479.                     vs[g->nvs].flags = 0;
  3480.  
  3481.                 if (ge->iy3 != ge->prev->iy3 || ny != ge->iy3)
  3482.                     g->nvs++;
  3483.             }
  3484.         }
  3485.     }
  3486.  
  3487.     g->nhs=addbluestems(hs, g->nhs);
  3488.     sortstems(hs, g->nhs);
  3489.     sortstems(vs, g->nvs);
  3490.  
  3491.     if (ISDBG(STEMS))
  3492.         debugstems(g->name, hs, g->nhs, vs, g->nvs);
  3493.  
  3494.     /* find the stems interacting with the Blue Zones */
  3495.     markbluestems(hs, g->nhs);
  3496.  
  3497.     if(subhints) {
  3498.         if (ISDBG(SUBSTEMS))
  3499.             fprintf(pfa_file, "%% %s: joining subst horizontal stems\n", g->name);
  3500.         joinsubstems(hs, hs_pairs, g->nhs, 1);
  3501.         uniformstems(hs, hs_pairs, g->nhs);
  3502.  
  3503.         if (ISDBG(SUBSTEMS))
  3504.             fprintf(pfa_file, "%% %s: joining subst vertical stems\n", g->name);
  3505.         joinsubstems(vs, vs_pairs, g->nvs, 0);
  3506.  
  3507.         groupsubstems(g, hs, hs_pairs, g->nhs, vs, vs_pairs, g->nvs);
  3508.     }
  3509.  
  3510.     if (ISDBG(MAINSTEMS))
  3511.         fprintf(pfa_file, "%% %s: joining main horizontal stems\n", g->name);
  3512.     g->nhs = joinmainstems(hs, g->nhs, 1);
  3513.     if (ISDBG(MAINSTEMS))
  3514.         fprintf(pfa_file, "%% %s: joining main vertical stems\n", g->name);
  3515.     g->nvs = joinmainstems(vs, g->nvs, 0);
  3516.  
  3517.     if (ISDBG(MAINSTEMS))
  3518.         debugstems(g->name, hs, g->nhs, vs, g->nvs);
  3519.  
  3520.     if(g->nhs > 0) {
  3521.         if ((sp = malloc(sizeof(STEM) * g->nhs)) == 0) {
  3522.             fprintf(stderr, "**** not enough memory for hints ****\n");
  3523.             exit(255);
  3524.         }
  3525.         g->hstems = sp;
  3526.         memcpy(sp, hs, sizeof(STEM) * g->nhs);
  3527.     } else
  3528.         g->hstems = 0;
  3529.  
  3530.     if(g->nvs > 0) {
  3531.         if ((sp = malloc(sizeof(STEM) * g->nvs)) == 0) {
  3532.             fprintf(stderr, "**** not enough memory for hints ****\n");
  3533.             exit(255);
  3534.         }
  3535.         g->vstems = sp;
  3536.         memcpy(sp, vs, sizeof(STEM) * g->nvs);
  3537.     } else
  3538.         g->vstems = 0;
  3539.  
  3540.     /* now check that the stems won't overflow the interpreter's stem stack:
  3541.      * some interpreters (like X11) push the stems on each change into
  3542.      * stack and pop them only after the whole glyphs is completed.
  3543.      */
  3544.  
  3545.     totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */
  3546.     lastgrp = -1;
  3547.  
  3548.     for (ge = g->entries; ge != 0; ge = ge->next) {
  3549.         grp=ge->stemid;
  3550.         if(grp >= 0 && grp != lastgrp)  {
  3551.             if(grp==0)
  3552.                 totals += g->nsbs[0];
  3553.             else
  3554.                 totals += g->nsbs[grp] - g->nsbs[grp-1];
  3555.  
  3556.             lastgrp = grp;
  3557.         }
  3558.     }
  3559.  
  3560.     /* be on the safe side, check for >= , not > */
  3561.     if(totals >= max_stemdepth) {  /* oops, too deep */
  3562.         WARNING_2 {
  3563.             fprintf(stderr, "Warning: glyph %s needs hint stack depth %d\n", g->name, totals);
  3564.             fprintf(stderr, "  (limit %d): removed the substituted hints from it\n", max_stemdepth);
  3565.         }
  3566.         if(g->nsg > 0) {
  3567.             for (ge = g->entries; ge != 0; ge = ge->next)
  3568.                 ge->stemid = -1;
  3569.             free(g->sbstems); g->sbstems = 0;
  3570.             free(g->nsbs); g->nsbs = 0;
  3571.             g->nsg = 0;
  3572.         }
  3573.     }
  3574.  
  3575.     /* now check if there are too many main stems */
  3576.     totals = (g->nhs+g->nvs) / 2; /* we count whole stems, not halves */
  3577.     if(totals >= max_stemdepth) { 
  3578.         /* even worse, too much of non-substituted stems */
  3579.         WARNING_2 {
  3580.             fprintf(stderr, "Warning: glyph %s has %d main hints\n", g->name, totals);
  3581.             fprintf(stderr, "  (limit %d): removed the hints from it\n", max_stemdepth);
  3582.         }
  3583.         if(g->vstems) {
  3584.             free(g->vstems); g->vstems = 0; g->nvs = 0;
  3585.         }
  3586.         if(g->hstems) {
  3587.             free(g->hstems); g->hstems = 0; g->nhs = 0;
  3588.         }
  3589.     }
  3590. }
  3591.  
  3592. /* convert weird curves that are close to lines into lines.
  3593. */
  3594.  
  3595. void
  3596. fstraighten(
  3597.        GLYPH * g
  3598. )
  3599. {
  3600.     GENTRY         *ge, *pge, *nge, *ige;
  3601.     double          df;
  3602.     int             dir;
  3603.     double          iln, oln;
  3604.     int             svdir, i, o;
  3605.  
  3606.     for (ige = g->entries; ige != 0; ige = ige->next) {
  3607.         if (ige->type != GE_CURVE)
  3608.             continue;
  3609.  
  3610.         ge = ige;
  3611.         pge = ge->bkwd;
  3612.         nge = ge->frwd;
  3613.  
  3614.         df = 0.;
  3615.  
  3616.         /* look for vertical then horizontal */
  3617.         for(i=0; i<2; i++) {
  3618.             o = !i; /* other axis */
  3619.  
  3620.             iln = fabs(ge->fpoints[i][2] - pge->fpoints[i][2]);
  3621.             oln = fabs(ge->fpoints[o][2] - pge->fpoints[o][2]);
  3622.             /*
  3623.              * if current curve is almost a vertical line, and it
  3624.              * doesn't begin or end horizontally (and the prev/next
  3625.              * line doesn't join smoothly ?)
  3626.              */
  3627.             if( oln < 1.
  3628.             || ge->fpoints[o][2] == ge->fpoints[o][1] 
  3629.             || ge->fpoints[o][0] == pge->fpoints[o][2]
  3630.             || iln > 2.
  3631.             || iln > 1.  && iln/oln > 0.1 )
  3632.                 continue;
  3633.  
  3634.  
  3635.             if(ISDBG(STRAIGHTEN)) 
  3636.                 fprintf(stderr,"** straighten almost %s\n", (i? "horizontal":"vertical"));
  3637.  
  3638.             df = ge->fpoints[i][2] - pge->fpoints[i][2];
  3639.             dir = fsign(ge->fpoints[o][2] - pge->fpoints[o][2]);
  3640.             ge->type = GE_LINE;
  3641.  
  3642.             /*
  3643.              * suck in all the sequence of such almost lines
  3644.              * going in the same direction but not deviating
  3645.              * too far from vertical
  3646.              */
  3647.             iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]);
  3648.             oln = nge->fpoints[o][2] - ge->fpoints[o][2];
  3649.  
  3650.             while (fabs(df) <= 5 && nge->type == GE_CURVE
  3651.             && dir == fsign(oln) /* that also gives oln != 0 */
  3652.             && iln <= 2.
  3653.             && ( iln <= 1.  || iln/fabs(oln) <= 0.1 ) ) {
  3654.                 ge->fx3 = nge->fx3;
  3655.                 ge->fy3 = nge->fy3;
  3656.  
  3657.                 if(ISDBG(STRAIGHTEN))
  3658.                     fprintf(stderr,"** straighten collapsing %s\n", (i? "horizontal":"vertical"));
  3659.                 freethisge(nge);
  3660.                 fixendpath(ge);
  3661.                 pge = ge->bkwd;
  3662.                 nge = ge->frwd;
  3663.  
  3664.                 df = ge->fpoints[i][2] - pge->fpoints[i][2];
  3665.  
  3666.                 iln = fabs(nge->fpoints[i][2] - ge->fpoints[i][2]);
  3667.                 oln = nge->fpoints[o][2] - ge->fpoints[o][2];
  3668.             }
  3669.  
  3670.             /* now check what do we have as previous/next line */
  3671.  
  3672.             if(ge != pge) { 
  3673.                 if( pge->type == GE_LINE && pge->fpoints[i][2] == pge->prev->fpoints[i][2]
  3674.                 && fabs(pge->fpoints[o][2] != pge->prev->fpoints[o][2]) ) {
  3675.                     if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with previous 0x%x 0x%x\n", pge, ge);
  3676.                     /* join the previous line with current */
  3677.                     pge->fx3 = ge->fx3;
  3678.                     pge->fy3 = ge->fy3;
  3679.  
  3680.                     ige = freethisge(ge)->prev; /* keep the iterator valid */
  3681.                     ge = pge;
  3682.                     fixendpath(ge);
  3683.                     pge = ge->bkwd;
  3684.                 }
  3685.             }
  3686.  
  3687.             if(ge != nge) { 
  3688.                 if (nge->type == GE_LINE && nge->fpoints[i][2] == ge->fpoints[i][2]
  3689.                 && fabs(nge->fpoints[o][2] != ge->fpoints[o][2]) ) {
  3690.                     if(ISDBG(STRAIGHTEN)) fprintf(stderr,"** straighten join with next 0x%x 0x%x\n", ge, nge);
  3691.                     /* join the next line with current */
  3692.                     ge->fx3 = nge->fx3;
  3693.                     ge->fy3 = nge->fy3;
  3694.  
  3695.                     freethisge(nge);
  3696.                     fixendpath(ge);
  3697.                     pge = ge->bkwd;
  3698.                     nge = ge->frwd;
  3699.  
  3700.                 }
  3701.             }
  3702.  
  3703.             if(ge != pge) { 
  3704.                 /* try to align the lines if neccessary */
  3705.                 if(df != 0.)
  3706.                     fclosegap(ge, ge, i, df, NULL);
  3707.             } else {
  3708.                 /* contour consists of only one line, get rid of it */
  3709.                 ige = freethisge(ge)->prev; /* keep the iterator valid */
  3710.             }
  3711.  
  3712.             break; /* don't bother looking at the other axis */
  3713.         }
  3714.     }
  3715. }
  3716.  
  3717. /* solve a square equation,
  3718.  * returns the number of solutions found, the solutions
  3719.  * are stored in res which should point to array of two doubles.
  3720.  * min and max limit the area for solutions
  3721.  */
  3722.  
  3723. static int
  3724. fsqequation(
  3725.     double a,
  3726.     double b,
  3727.     double c,
  3728.     double *res,
  3729.     double min,
  3730.     double max
  3731. )
  3732. {
  3733.     double D;
  3734.     int n;
  3735.  
  3736.     if(ISDBG(SQEQ)) fprintf(stderr, "sqeq(%g,%g,%g) [%g;%g]\n", a, b, c, min, max);
  3737.  
  3738.     if(fabs(a) < 0.000001) { /* if a linear equation */
  3739.         n=0;
  3740.         if(fabs(b) < 0.000001) /* not an equation at all */
  3741.             return 0;
  3742.         res[0] = -c/b;
  3743.         if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: linear t=%g\n", res[0]);
  3744.         if(res[0] >= min && res[0] <= max)
  3745.             n++;
  3746.         return n;
  3747.     }
  3748.  
  3749.     D = b*b - 4.0*a*c;
  3750.     if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: D=%g\n", D);
  3751.     if(D<0)
  3752.         return 0;
  3753.  
  3754.     D = sqrt(D);
  3755.  
  3756.     n=0;
  3757.     res[0] = (-b+D) / (2*a);
  3758.     if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t1=%g\n", res[0]);
  3759.     if(res[0] >= min && res[0] <= max)
  3760.         n++;
  3761.  
  3762.     res[n] = (-b-D) / (2*a);
  3763.     if(ISDBG(SQEQ)) fprintf(stderr, "sqeq: t2=%g\n", res[n]);
  3764.     if(res[n] >= min && res[n] <= max)
  3765.         n++;
  3766.  
  3767.     /* return 2nd solution only if it's different enough */
  3768.     if(n==2 && fabs(res[0]-res[1])<0.000001)
  3769.         n=1;
  3770.  
  3771.     return n;
  3772. }
  3773.  
  3774. /* check that the curves don't cross quadrant boundary */
  3775. /* (float) */
  3776.  
  3777. /*
  3778.   Here we make sure that the curve does not continue past
  3779.   horizontal or vertical extremums. The horizontal points are
  3780.   explained, vertical points are by analogy.
  3781.  
  3782.   The horizontal points are where the derivative
  3783.   dy/dx is equal to 0. But the Bezier curves are defined by
  3784.   parametric formulas
  3785.    x=fx(t)
  3786.    y=fy(t)
  3787.   so finding this derivative is complicated.
  3788.   Also even if we find some point (x,y) splitting at this point
  3789.   is far not obvious. Fortunately we can use dy/dt = 0 instead,
  3790.   this gets to a rather simple square equation and splitting
  3791.   at a known value of t is simple.
  3792.  
  3793.   The formulas are:
  3794.  
  3795.   y = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3
  3796.   y = (-A+3*B-3*C+D)*t^3 + (3*A-6*B+3*C)*t^2 + (-3*A+3*B)*t + A
  3797.   dy/dt = 3*(-A+3*B-3*C+D)*t^2 + 2*(3*A-6*B+3*C)*t + (-3*A+3*B)
  3798.  */
  3799.  
  3800. void
  3801. ffixquadrants(
  3802.     GLYPH *g
  3803. )
  3804. {
  3805.     GENTRY         *ge, *nge;
  3806.     int    i, j, np, oldnp;
  3807.     double    sp[5]; /* split points, last one empty */
  3808.     char dir[5]; /* for debugging, direction by which split happened */
  3809.     double a, b, *pts; /* points of a curve */
  3810.  
  3811.     for (ge = g->entries; ge != 0; ge = ge->next) {
  3812.         if (ge->type != GE_CURVE)
  3813.             continue;
  3814.         
  3815.     doagain:
  3816.         np = 0; /* no split points yet */
  3817.         if(ISDBG(QUAD)) {
  3818.             fprintf(stderr, "%s: trying 0x%x (%g %g) (%g %g) (%g %g) (%g %g)\n  ", g->name,
  3819.                 ge,  ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2,
  3820.                 ge->fx3, ge->fy3);
  3821.         }
  3822.         for(i=0; i<2; i++) { /* first for x then for y */
  3823.             /* find the cooridnates of control points */
  3824.             a = ge->prev->fpoints[i][2];
  3825.             pts = &ge->fpoints[i][0];
  3826.  
  3827.             oldnp = np;
  3828.             np += fsqequation(
  3829.                 3.0*(-a + 3.0*pts[0] - 3.0*pts[1] + pts[2]),
  3830.                 6.0*(a - 2.0*pts[0] + pts[1]),
  3831.                 3.0*(-a + pts[0]),
  3832.                 &sp[np],
  3833.                 0.0, 1.0); /* XXX range is [0;1] */
  3834.  
  3835.             if(np == oldnp)
  3836.                 continue;
  3837.  
  3838.             if(ISDBG(QUAD))
  3839.                 fprintf(stderr, "%s: 0x%x: %d pts(%c): ", 
  3840.                     g->name, ge, np-oldnp, i? 'y':'x');
  3841.  
  3842.             /* remove points that are too close to the ends 
  3843.              * because hor/vert ends are permitted, also
  3844.              * if the split point is VERY close to the ends
  3845.              * but not exactly then just flatten it and check again.
  3846.              */
  3847.             for(j = oldnp; j<np; j++) {
  3848.                 dir[j] = i;
  3849.                 if(ISDBG(QUAD))
  3850.                     fprintf(stderr, "%g ", sp[j]);
  3851.                 if(sp[j] < 0.03) { /* front end of curve */
  3852.                     if(ge->fpoints[i][0] != ge->prev->fpoints[i][2]) {
  3853.                         ge->fpoints[i][0] = ge->prev->fpoints[i][2];
  3854.                         if(ISDBG(QUAD)) fprintf(stderr, "flattened at front\n");
  3855.                         goto doagain;
  3856.                     }
  3857.                     if( ge->fpoints[i][1] != ge->fpoints[i][0]
  3858.                     && fsign(ge->fpoints[i][2] - ge->fpoints[i][1])
  3859.                             != fsign(ge->fpoints[i][1] - ge->fpoints[i][0]) ) {
  3860.                         ge->fpoints[i][1] = ge->fpoints[i][0];
  3861.                         if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at front\n");
  3862.                         goto doagain;
  3863.                     }
  3864.                     sp[j] = sp[j+1]; np--; j--;
  3865.                     if(ISDBG(QUAD)) fprintf(stderr, "(front flat)  ");
  3866.                 } else if(sp[j] > 0.97) { /* rear end of curve */
  3867.                     if(ge->fpoints[i][1] != ge->fpoints[i][2]) {
  3868.                         ge->fpoints[i][1] = ge->fpoints[i][2];
  3869.                         if(ISDBG(QUAD)) fprintf(stderr, "flattened at rear\n");
  3870.                         goto doagain;
  3871.                     }
  3872.                     if( ge->fpoints[i][0] != ge->fpoints[i][1]
  3873.                     && fsign(ge->prev->fpoints[i][2] - ge->fpoints[i][0])
  3874.                             != fsign(ge->fpoints[i][0] - ge->fpoints[i][1]) ) {
  3875.                         ge->fpoints[i][0] = ge->fpoints[i][1];
  3876.                         if(ISDBG(QUAD)) fprintf(stderr, "flattened zigzag at rear\n");
  3877.                         goto doagain;
  3878.                     }
  3879.                     sp[j] = sp[j+1]; np--; j--;
  3880.                     if(ISDBG(QUAD)) fprintf(stderr, "(rear flat)  ");
  3881.                 } 
  3882.             }
  3883.             if(ISDBG(QUAD)) fprintf(stderr, "\n");
  3884.         }
  3885.  
  3886.         if(np==0) /* no split points, leave it alone */
  3887.             continue;
  3888.  
  3889.         if(ISDBG(QUAD)) {
  3890.             fprintf(stderr, "%s: splitting 0x%x (%g %g) (%g %g) (%g %g) (%g %g) at %d points\n  ", g->name,
  3891.                 ge,  ge->prev->fx3, ge->prev->fy3, ge->fx1, ge->fy1, ge->fx2, ge->fy2,
  3892.                 ge->fx3, ge->fy3, np);
  3893.             for(i=0; i<np; i++)
  3894.                 fprintf(stderr, "%g(%c) ", sp[i], dir[i] ? 'y':'x');
  3895.             fprintf(stderr, "\n");
  3896.         }
  3897.  
  3898.         /* sort the points ascending */
  3899.         for(i=0; i<np; i++)
  3900.             for(j=i+1; j<np; j++)
  3901.                 if(sp[i] > sp[j]) {
  3902.                     a = sp[i]; sp[i] = sp[j]; sp[j] = a;
  3903.                 }
  3904.  
  3905.         /* now finally do the split on each point */
  3906.         for(j=0; j<np; j++) {
  3907.             double k1, k2, c;
  3908.  
  3909.             k1 = sp[j];
  3910.             k2 = 1 - k1;
  3911.  
  3912.             if(ISDBG(QUAD)) fprintf(stderr, "   0x%x %g/%g\n", ge, k1, k2);
  3913.  
  3914.             nge = newgentry(GEF_FLOAT);
  3915.             (*nge) = (*ge);
  3916.  
  3917. #define SPLIT(pt1, pt2)    ( (pt1) + k1*((pt2)-(pt1)) ) /* order is important! */
  3918.             for(i=0; i<2; i++) { /* for x and y */
  3919.                 a = ge->fpoints[i][0]; /* get the middle points */
  3920.                 b = ge->fpoints[i][1];
  3921.  
  3922.                 /* calculate new internal points */
  3923.                 c = SPLIT(a, b);
  3924.  
  3925.                 ge->fpoints[i][0] = SPLIT(ge->prev->fpoints[i][2], a);
  3926.                 ge->fpoints[i][1] = SPLIT(ge->fpoints[i][0], c);
  3927.  
  3928.                 nge->fpoints[i][1] = SPLIT(b, nge->fpoints[i][2]);
  3929.                 nge->fpoints[i][0] = SPLIT(c, nge->fpoints[i][1]);
  3930.  
  3931.                 ge->fpoints[i][2] = SPLIT(ge->fpoints[i][1],
  3932.                     + nge->fpoints[i][0]);
  3933.             }
  3934. #undef SPLIT
  3935.  
  3936.             addgeafter(ge, nge);
  3937.  
  3938.             /* go to the next part, adjust remaining points */
  3939.             ge = nge;
  3940.             for(i=j+1; i<np; i++)
  3941.                 sp[i] = (sp[i]-k1) / k2;
  3942.         }
  3943.     }
  3944.  
  3945. }
  3946.  
  3947. /* check if a curve is a zigzag */
  3948.  
  3949. static int
  3950. iiszigzag(
  3951.     GENTRY *ge
  3952. {
  3953.     double          k, k1, k2;
  3954.     double          a, b;
  3955.  
  3956.     if (ge->type != GE_CURVE)
  3957.         return 0;
  3958.  
  3959.     a = ge->iy2 - ge->iy1;
  3960.     b = ge->ix2 - ge->ix1;
  3961.     k = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a);
  3962.     a = ge->iy1 - ge->prev->iy3;
  3963.     b = ge->ix1 - ge->prev->ix3;
  3964.     k1 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a);
  3965.     a = ge->iy3 - ge->iy2;
  3966.     b = ge->ix3 - ge->ix2;
  3967.     k2 = fabs(a == 0 ? (b == 0 ? 1. : FBIGVAL) : (double) b / (double) a);
  3968.  
  3969.     /* if the curve is not a zigzag */
  3970.     if (k1 >= k && k2 <= k || k1 <= k && k2 >= k)
  3971.         return 0;
  3972.     else
  3973.         return 1;
  3974. }
  3975.  
  3976. /* check if a curve is a zigzag - floating */
  3977.  
  3978. static int
  3979. fiszigzag(
  3980.     GENTRY *ge
  3981. {
  3982.     double          k, k1, k2;
  3983.     double          a, b;
  3984.  
  3985.     if (ge->type != GE_CURVE)
  3986.         return 0;
  3987.  
  3988.     a = fabs(ge->fy2 - ge->fy1);
  3989.     b = fabs(ge->fx2 - ge->fx1);
  3990.     k = a < FEPS ? (b <FEPS ? 1. : FBIGVAL) : b / a;
  3991.     a = fabs(ge->fy1 - ge->prev->fy3);
  3992.     b = fabs(ge->fx1 - ge->prev->fx3);
  3993.     k1 = a < FEPS ? (b < FEPS ? 1. : FBIGVAL) : b / a;
  3994.     a = fabs(ge->fy3 - ge->fy2);
  3995.     b = fabs(ge->fx3 - ge->fx2);
  3996.     k2 = a < FEPS ? (b <FEPS ? 1. : FBIGVAL) : b / a;
  3997.  
  3998.     /* if the curve is not a zigzag */
  3999.     if (k1 >= k && k2 <= k || k1 <= k && k2 >= k)
  4000.         return 0;
  4001.     else
  4002.         return 1;
  4003. }
  4004.  
  4005. /* split the zigzag-like curves into two parts */
  4006.  
  4007. void
  4008. fsplitzigzags(
  4009.          GLYPH * g
  4010. )
  4011. {
  4012.     GENTRY         *ge, *nge;
  4013.     double          a, b, c, d;
  4014.  
  4015.     assertisfloat(g, "splitting zigzags");
  4016.     for (ge = g->entries; ge != 0; ge = ge->next) {
  4017.         if (ge->type != GE_CURVE)
  4018.             continue;
  4019.  
  4020.         /* if the curve is not a zigzag */
  4021.         if ( !fiszigzag(ge) ) {
  4022.             continue;
  4023.         }
  4024.  
  4025.         /* split the curve by t=0.5 */
  4026.         nge = newgentry(GEF_FLOAT);
  4027.         (*nge) = (*ge);
  4028.         nge->type = GE_CURVE;
  4029.  
  4030.         a = ge->prev->fx3;
  4031.         b = ge->fx1;
  4032.         c = ge->fx2;
  4033.         d = ge->fx3;
  4034.         nge->fx3 = d;
  4035.         nge->fx2 = (c + d) / 2.;
  4036.         nge->fx1 = (b + 2. * c + d) / 4.;
  4037.         ge->fx3 = (a + b * 3. + c * 3. + d) / 8.;
  4038.         ge->fx2 = (a + 2. * b + c) / 4.;
  4039.         ge->fx1 = (a + b) / 2.;
  4040.  
  4041.         a = ge->prev->fy3;
  4042.         b = ge->fy1;
  4043.         c = ge->fy2;
  4044.         d = ge->fy3;
  4045.         nge->fy3 = d;
  4046.         nge->fy2 = (c + d) / 2.;
  4047.         nge->fy1 = (b + 2. * c + d) / 4.;
  4048.         ge->fy3 = (a + b * 3. + c * 3. + d) / 8.;
  4049.         ge->fy2 = (a + 2. * b + c) / 4.;
  4050.         ge->fy1 = (a + b) / 2.;
  4051.  
  4052.         addgeafter(ge, nge);
  4053.     }
  4054. }
  4055.  
  4056. /* free this GENTRY, returns what was ge->next
  4057.  * (ge must be of type GE_LINE or GE_CURVE)
  4058.  * works on both float and int entries
  4059.  */
  4060.  
  4061. static GENTRY *
  4062. freethisge(
  4063.     GENTRY *ge
  4064. )
  4065. {
  4066.     GENTRY *xge;
  4067.  
  4068.     if (ge->bkwd != ge->prev) {
  4069.         /* at beginning of the contour */
  4070.  
  4071.         xge = ge->bkwd;
  4072.         if(xge == ge) { /* was the only line in contour */
  4073.             /* remove the contour completely */
  4074.             /* prev is GE_MOVE, next is GE_PATH, remove them all */
  4075.  
  4076.             /* may be the first contour, then ->bkwd points to ge->entries */
  4077.             if(ge->prev->prev == 0)
  4078.                 *(GENTRY **)(ge->prev->bkwd) = ge->next->next;
  4079.             else
  4080.                 ge->prev->prev->next = ge->next->next;
  4081.  
  4082.             if(ge->next->next) {
  4083.                 ge->next->next->prev = ge->prev->prev;
  4084.                 ge->next->next->bkwd = ge->prev->bkwd;
  4085.             }
  4086.  
  4087.             xge = ge->next->next;
  4088.             free(ge->prev); free(ge->next); free(ge);
  4089.             return xge;
  4090.         }
  4091.  
  4092.         /* move the start point of the contour */
  4093.         if(ge->flags & GEF_FLOAT) {
  4094.             ge->prev->fx3 = xge->fx3;
  4095.             ge->prev->fy3 = xge->fy3;
  4096.         } else {
  4097.             ge->prev->ix3 = xge->ix3;
  4098.             ge->prev->iy3 = xge->iy3;
  4099.         }
  4100.     } else if(ge->frwd != ge->next) {
  4101.         /* at end of the contour */
  4102.  
  4103.         xge = ge->frwd->prev;
  4104.         /* move the start point of the contour */
  4105.         if(ge->flags & GEF_FLOAT) {
  4106.             xge->fx3 = ge->bkwd->fx3;
  4107.             xge->fy3 = ge->bkwd->fy3;
  4108.         } else {
  4109.             xge->ix3 = ge->bkwd->ix3;
  4110.             xge->iy3 = ge->bkwd->iy3;
  4111.         }
  4112.     }
  4113.  
  4114.     ge->prev->next = ge->next;
  4115.     ge->next->prev = ge->prev;
  4116.     ge->bkwd->frwd = ge->frwd;
  4117.     ge->frwd->bkwd = ge->bkwd;
  4118.  
  4119.     xge = ge->next;
  4120.     free(ge);
  4121.     return xge;
  4122. }
  4123.  
  4124. /* inserts a new gentry (LINE or CURVE) after another (MOVE
  4125.  * or LINE or CURVE)
  4126.  * corrects the first GE_MOVE if neccessary
  4127.  */
  4128.  
  4129. static void
  4130. addgeafter(
  4131.     GENTRY *oge, /* after this */
  4132.     GENTRY *nge /* insert this */
  4133. )
  4134. {
  4135.     if(oge->type == GE_MOVE) {
  4136.         /* insert before next */
  4137.         if(oge->next->type == GE_PATH) {
  4138.             /* first and only GENTRY in path */
  4139.             nge->frwd = nge->bkwd = nge;
  4140.         } else {
  4141.             nge->frwd = oge->next;
  4142.             nge->bkwd = oge->next->bkwd;
  4143.             oge->next->bkwd->frwd = nge;
  4144.             oge->next->bkwd = nge;
  4145.         }
  4146.     } else {
  4147.         nge->frwd = oge->frwd;
  4148.         nge->bkwd = oge;
  4149.         oge->frwd->bkwd = nge;
  4150.         oge->frwd = nge;
  4151.     }
  4152.  
  4153.     nge->next = oge->next;
  4154.     nge->prev = oge;
  4155.     oge->next->prev = nge;
  4156.     oge->next = nge;
  4157.  
  4158.     if(nge->frwd->prev->type == GE_MOVE) {
  4159.         /* fix up the GE_MOVE entry */
  4160.         if(nge->flags & GEF_FLOAT) {
  4161.             nge->frwd->prev->fx3 = nge->fx3;
  4162.             nge->frwd->prev->fy3 = nge->fy3;
  4163.         } else {
  4164.             nge->frwd->prev->ix3 = nge->ix3;
  4165.             nge->frwd->prev->iy3 = nge->iy3;
  4166.         }
  4167.     }
  4168. }
  4169.  
  4170. /*
  4171.  * Check if this GENTRY happens to be at the end of path
  4172.  * and fix the first MOVETO accordingly
  4173.  * handles both int and float
  4174.  */
  4175.  
  4176. static void
  4177. fixendpath(
  4178.     GENTRY *ge
  4179. )
  4180. {
  4181.     GENTRY *mge;
  4182.  
  4183.     mge = ge->frwd->prev;
  4184.     if(mge->type == GE_MOVE) {
  4185.         if(ge->flags & GEF_FLOAT) {
  4186.             mge->fx3 = ge->fx3;
  4187.             mge->fy3 = ge->fy3;
  4188.         } else {
  4189.             mge->ix3 = ge->ix3;
  4190.             mge->iy3 = ge->iy3;
  4191.         }
  4192.     }
  4193. }
  4194.  
  4195. /*
  4196.  * This function adjusts the rest of path (the part from...to is NOT changed)
  4197.  * to cover the specified gap by the specified axis (0 - X, 1 - Y).
  4198.  * Gap is counted in direction (end_of_to - beginning_of_from).
  4199.  * Returns by how much the gap was not closed (0.0 if it was fully closed).
  4200.  * Ret contains by how much the first and last points of [from...to]
  4201.  * were moved to bring them in consistence to the rest of the path.
  4202.  * If ret==NULL then this info is not returned.
  4203.  */
  4204.  
  4205. static double
  4206. fclosegap(
  4207.     GENTRY *from,
  4208.     GENTRY *to,
  4209.     int axis,
  4210.     double gap,
  4211.     double *ret
  4212. )
  4213. {
  4214. #define TIMESLARGER 10.    /* how many times larger must be a curve to not change too much */
  4215.     double rm[2];
  4216.     double oldpos[2];
  4217.     double times, limit, df, dx;
  4218.     int j, k;
  4219.     GENTRY *xge, *pge, *nge, *bge[2];
  4220.  
  4221.     /* remember the old points to calculate ret */
  4222.     oldpos[0] = from->prev->fpoints[axis][2];
  4223.     oldpos[1] = to->fpoints[axis][2];
  4224.  
  4225.     rm[0] = rm[1] = gap / 2. ;
  4226.  
  4227.     bge[0] = from; /* this is convenient for iterations */
  4228.     bge[1] = to;
  4229.  
  4230.     /* first try to modify large curves but if have none then settle for small */
  4231.     for(times = (TIMESLARGER-1); times > 0.1; times /= 2. ) {
  4232.  
  4233.         if(rm[0]+rm[1] == 0.)
  4234.             break;
  4235.  
  4236.         /* iterate in both directions, backwards then forwards */
  4237.         for(j = 0; j<2; j++) {
  4238.  
  4239.             if(rm[j] == 0.) /* if this direction is exhausted */
  4240.                 continue;
  4241.  
  4242.             limit = fabs(rm[j]) * (1.+times);
  4243.  
  4244.             for(xge = bge[j]->cntr[j]; xge != bge[!j]; xge = xge->cntr[j]) {
  4245.                 dx = xge->fpoints[axis][2] - xge->prev->fpoints[axis][2];
  4246.                 df = fabs(dx) - limit;
  4247.                 if( df <= FEPS ) /* curve is too small to change */
  4248.                     continue;
  4249.  
  4250.                 if( df >= fabs(rm[j]) )
  4251.                     df = rm[j];
  4252.                 else 
  4253.                     df *= fsign(rm[j]); /* we may cover this part of rm */
  4254.  
  4255.                 rm[j] -= df;
  4256.                 limit = fabs(rm[j]) * (1.+times);
  4257.  
  4258.                 if(xge->type == GE_CURVE) { /* correct internal points */
  4259.                     double scale = ((dx+df) / dx) - 1.;
  4260.                     double base;
  4261.  
  4262.                     if(j)
  4263.                         base = xge->fpoints[axis][2];
  4264.                     else
  4265.                         base = xge->prev->fpoints[axis][2];
  4266.  
  4267.                     for(k = 0; k<2; k++)
  4268.                         xge->fpoints[axis][k] += scale * 
  4269.                             (xge->fpoints[axis][k] - base);
  4270.                 }
  4271.  
  4272.                 /* move all the intermediate lines */
  4273.                 if(j) {
  4274.                     df = -df; /* absolute direction */
  4275.                     pge = bge[1]->bkwd;
  4276.                     nge = xge->bkwd;
  4277.                 } else {
  4278.                     xge->fpoints[axis][2] += df;
  4279.                     pge = bge[0];
  4280.                     nge = xge->frwd;
  4281.                 }
  4282.                 while(nge != pge) {
  4283.                     if(nge->type == GE_CURVE) {
  4284.                         nge->fpoints[axis][0] +=df;
  4285.                         nge->fpoints[axis][1] +=df;
  4286.                     }
  4287.                     nge->fpoints[axis][2] += df;
  4288.                     if(nge->next != nge->frwd) { /* last entry of contour */
  4289.                         nge->frwd->prev->fpoints[axis][2] += df;
  4290.                     }
  4291.                     nge = nge->cntr[!j];
  4292.                 }
  4293.  
  4294.                 if(rm[j] == 0.)
  4295.                     break;
  4296.             }
  4297.         }
  4298.     }
  4299.  
  4300.     /* find the difference */
  4301.     oldpos[0] -= from->prev->fpoints[axis][2];
  4302.     oldpos[1] -= to->fpoints[axis][2];
  4303.  
  4304.     if(ret) {
  4305.         ret[0] = oldpos[0] - from->prev->fpoints[axis][2];
  4306.         ret[1] = oldpos[1] - to->fpoints[axis][2];
  4307.     }
  4308.  
  4309. #if 0
  4310.     if( rm[0]+rm[1] != gap - oldpos[1] + oldpos[0]) {
  4311.         fprintf(stderr, "** gap=%g rm[0]=%g rm[1]=%g o[0]=%g o[1]=%g rg=%g og=%g\n",
  4312.             gap, rm[0], rm[1], oldpos[0], oldpos[1], rm[0]+rm[1], 
  4313.             gap - oldpos[1] + oldpos[0]);
  4314.     }
  4315. #endif
  4316.  
  4317.     return rm[0]+rm[1];
  4318. #undef TIMESLARGER
  4319. }
  4320.  
  4321. /* remove the lines or curves smaller or equal to the size limit */
  4322.  
  4323. static void
  4324. fdelsmall(
  4325.     GLYPH *g,
  4326.     double minlen
  4327. )
  4328. {
  4329.     GENTRY  *ge, *nge, *pge, *xge, *next;
  4330.     int i, k;
  4331.     double dx, dy, d2, d2m;
  4332.     double minlen2;
  4333. #define TIMESLARGER 10.    /* how much larger must be a curve to not change too much */
  4334.  
  4335.     minlen2 = minlen*minlen;
  4336.  
  4337.     for (ge = g->entries; ge != 0; ge = next) {
  4338.         next = ge->next;
  4339.  
  4340.         if (ge->type != GE_CURVE && ge->type != GE_LINE)
  4341.             continue;
  4342.  
  4343.         d2m = 0;
  4344.         for(i= (ge->type==GE_CURVE? 0: 2); i<3; i++) {
  4345.             dx = ge->fxn[i] - ge->prev->fx3;
  4346.             dy = ge->fyn[i] - ge->prev->fy3;
  4347.             d2 = dx*dx + dy*dy;
  4348.             if(d2m < d2)
  4349.                 d2m = d2;
  4350.         }
  4351.  
  4352.         if( d2m > minlen2 ) { /* line is not too small */
  4353.             /* XXX add more normalization here */
  4354.             continue;
  4355.         }
  4356.  
  4357.         /* if the line is too small */
  4358.  
  4359.         /* check forwards if we have a whole sequence of them */
  4360.         nge = ge;
  4361.         for(xge = ge->frwd; xge != ge; xge = xge->frwd) {
  4362.             d2m = 0;
  4363.             for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) {
  4364.                 dx = xge->fxn[i] - xge->prev->fx3;
  4365.                 dy = xge->fyn[i] - xge->prev->fy3;
  4366.                 d2 = dx*dx + dy*dy;
  4367.                 if(d2m < d2)
  4368.                     d2m = d2;
  4369.             }
  4370.             if( d2m > minlen2 ) /* line is not too small */
  4371.                 break;
  4372.             nge = xge;
  4373.             if(next == nge) /* move the next step past this sequence */
  4374.                 next = next->next;
  4375.         }
  4376.  
  4377.         /* check backwards if we have a whole sequence of them */
  4378.         pge = ge;
  4379.         for(xge = ge->bkwd; xge != ge; xge = xge->bkwd) {
  4380.             d2m = 0;
  4381.             for(i= (xge->type==GE_CURVE? 0: 2); i<3; i++) {
  4382.                 dx = xge->fxn[i] - xge->prev->fx3;
  4383.                 dy = xge->fyn[i] - xge->prev->fy3;
  4384.                 d2 = dx*dx + dy*dy;
  4385.                 if(d2m < d2)
  4386.                     d2m = d2;
  4387.             }
  4388.             if( d2m > minlen2 ) /* line is not too small */
  4389.                 break;
  4390.             pge = xge;
  4391.         }
  4392.  
  4393.         /* now we have a sequence of small fragments in pge...nge (inclusive) */
  4394.  
  4395.         if(ISDBG(FCONCISE))  {
  4396.             fprintf(stderr, "glyph %s has very small fragments(%x..%x..%x)\n", 
  4397.             g->name, pge, ge, nge);
  4398.             dumppaths(g, pge, nge);
  4399.         }
  4400.  
  4401.         /* reduce whole sequence to one part and remember the middle point */
  4402.         if(pge != nge) {
  4403.             while(1) {
  4404.                 xge = pge->frwd;
  4405.                 if(xge == nge) {
  4406.                     pge->fx1 = pge->fx2 = pge->fx3;
  4407.                     pge->fx3 = nge->fx3;
  4408.                     pge->fy1 = pge->fy2 = pge->fy3;
  4409.                     pge->fy3 = nge->fy3;
  4410.                     pge->type = GE_CURVE;
  4411.                     freethisge(nge);
  4412.                     break;
  4413.                 }
  4414.                 if(xge == nge->bkwd) {
  4415.                     pge->fx1 = pge->fx2 = (pge->fx3+xge->fx3)/2.;
  4416.                     pge->fx3 = nge->fx3;
  4417.                     pge->fy1 = pge->fy2 = (pge->fy3+xge->fy3)/2.;
  4418.                     pge->fy3 = nge->fy3;
  4419.                     pge->type = GE_CURVE;
  4420.                     freethisge(nge);
  4421.                     freethisge(xge);
  4422.                     break;
  4423.                 }
  4424.                 freethisge(pge); pge = xge;
  4425.                 xge = nge->bkwd; freethisge(nge); nge = xge;
  4426.             }
  4427.         }
  4428.         ge = pge;
  4429.  
  4430.         /* check if the whole sequence is small */
  4431.         dx = ge->fx3 - ge->prev->fx3;
  4432.         dy = ge->fy3 - ge->prev->fy3;
  4433.         d2 = dx*dx + dy*dy;
  4434.  
  4435.         if( d2 > minlen2 ) { /* no, it is not */
  4436.             double b, d;
  4437.  
  4438.             WARNING_3 fprintf(stderr, "glyph %s had a sequence of fragments < %g points each, reduced to one curve\n",
  4439.                 g->name, minlen);
  4440.  
  4441.             /* check that we did not create a monstrosity spanning quadrants */
  4442.             if(fsign(ge->fx1 - ge->prev->fx1) * fsign(ge->fx3 - ge->fx1) < 0
  4443.             || fsign(ge->fy1 - ge->prev->fy1) * fsign(ge->fy3 - ge->fy1) < 0 ) { 
  4444.                 /* yes, we did; are both parts of this thing big enough ? */
  4445.                 dx = ge->fx1 - ge->prev->fx3;
  4446.                 dy = ge->fy1 - ge->prev->fy3;
  4447.                 d2 = dx*dx + dy*dy;
  4448.  
  4449.                 dx = ge->fx3 - ge->fx1;
  4450.                 dy = ge->fy3 - ge->fy1;
  4451.                 d2m = dx*dx + dy*dy;
  4452.  
  4453.                 if(d2 > minlen2 && d2m > minlen2) { /* make two straights */
  4454.                     nge = newgentry(GEF_FLOAT);
  4455.                     *nge = *ge;
  4456.                     
  4457.                     for(i=0; i<2; i++) {
  4458.                         ge->fpoints[i][2] = ge->fpoints[i][0];
  4459.                         b = nge->fpoints[i][0];
  4460.                         d = nge->fpoints[i][2] - b;
  4461.                         nge->fpoints[i][0] = b + 0.1*d;
  4462.                         nge->fpoints[i][1] = b + 0.9*d;
  4463.                     }
  4464.                 }
  4465.                 for(i=0; i<2; i++) { /* make one straight or first of two straights */
  4466.                     b = ge->prev->fpoints[i][2];
  4467.                     d = ge->fpoints[i][2] - b;
  4468.                     ge->fpoints[i][0] = b + 0.1*d;
  4469.                     ge->fpoints[i][1] = b + 0.9*d;
  4470.                 }
  4471.             }
  4472.             continue; 
  4473.         }
  4474.  
  4475.         if(ge->frwd == ge) { /* points to itself, just remove the path completely */
  4476.             WARNING_3 fprintf(stderr, "glyph %s had a path made of fragments < %g points each, removed\n",
  4477.                 g->name, minlen);
  4478.  
  4479.             next = freethisge(ge);
  4480.             continue;
  4481.         } 
  4482.  
  4483.         /* now close the gap by x and y */
  4484.         for(i=0; i<2; i++) {
  4485.             double gap;
  4486.  
  4487.             gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2];
  4488.             if( fclosegap(ge, ge, i, gap, NULL) != 0.0 ) {
  4489.                 double scale, base;
  4490.  
  4491.                 /* not good, as the last resort just scale the next line */
  4492.                 gap = ge->fpoints[i][2] - ge->prev->fpoints[i][2];
  4493.  
  4494.                 if(ISDBG(FCONCISE)) 
  4495.                     fprintf(stderr, "    last resort on %c: closing next by %g\n",
  4496.                     (i==0 ? 'x' : 'y'), gap);
  4497.  
  4498.                 nge = ge->frwd;
  4499.                 base = nge->fpoints[i][2];
  4500.                 dx = ge->fpoints[i][2] - base;
  4501.                 if(fabs(dx) < FEPS)
  4502.                     continue;
  4503.  
  4504.                 scale = ((dx-gap) / dx);
  4505.  
  4506.                 if(nge->type == GE_CURVE)
  4507.                     for(k = 0; k<2; k++)
  4508.                         nge->fpoints[i][k] = base + 
  4509.                             scale * (nge->fpoints[i][k] - base);
  4510.  
  4511.                 ge->fpoints[i][2] -= gap;
  4512.             }
  4513.         }
  4514.  
  4515.         /* OK, the gap is closed - remove this useless GENTRY */
  4516.         freethisge(ge);
  4517.     }
  4518. #undef TIMESLARGER
  4519. }
  4520.  
  4521. /* normalize curves to the form where their ends
  4522.  * can be safely used as derivatives
  4523.  */
  4524.  
  4525. static void
  4526. fnormalizec(
  4527.          GLYPH * g
  4528. )
  4529. {
  4530.     GENTRY *ge;
  4531.     int midsame, frontsame, rearsame, i;
  4532.     double d, b;
  4533.  
  4534.     assertisfloat(g, "normalizing curves");
  4535.  
  4536.     for (ge = g->entries; ge != 0; ge = ge->next) {
  4537.         if (ge->type != GE_CURVE)
  4538.             continue;
  4539.  
  4540.         midsame = (fabs(ge->fx1-ge->fx2)<FEPS && fabs(ge->fy1-ge->fy2)<FEPS);
  4541.         frontsame = (fabs(ge->fx1-ge->prev->fx3)<FEPS && fabs(ge->fy1-ge->prev->fy3)<FEPS);
  4542.         rearsame = (fabs(ge->fx3-ge->fx2)<FEPS && fabs(ge->fy3-ge->fy2)<FEPS);
  4543.  
  4544.         if(midsame && (frontsame || rearsame) ) {
  4545.             /* essentially a line */
  4546.             for(i=0; i<2; i++) {
  4547.                 b = ge->prev->fpoints[i][2];
  4548.                 d = ge->fpoints[i][2] - b;
  4549.                 ge->fpoints[i][0] = b + 0.1*d;
  4550.                 ge->fpoints[i][1] = b + 0.9*d;
  4551.             }
  4552.         } else if(frontsame) {
  4553.             for(i=0; i<2; i++) {
  4554.                 b = ge->prev->fpoints[i][2];
  4555.                 d = ge->fpoints[i][1] - b;
  4556.                 ge->fpoints[i][0] = b + 0.01*d;
  4557.             }
  4558.         } else if(rearsame) {
  4559.             for(i=0; i<2; i++) {
  4560.                 b = ge->fpoints[i][2];
  4561.                 d = ge->fpoints[i][0] - b;
  4562.                 ge->fpoints[i][1] = b + 0.01*d;
  4563.             }
  4564.         } else
  4565.             continue;
  4566.  
  4567.         if(ISDBG(FCONCISE)) fprintf(stderr, "glyph %g, normalized entry %x\n", g->name, ge);
  4568.     }
  4569. }
  4570.  
  4571. /* find the point where two rays continuing vectors cross
  4572.  * rays are defined as beginning of curve1 and end of curve 2
  4573.  * returns 1 if they cross, 0 if they don't
  4574.  * If they cross returns the maximal scales for both vectors.
  4575.  * Expects that the curves are normalized.
  4576.  */
  4577.  
  4578. static int
  4579. fcrossrays(
  4580.     GENTRY *ge1,
  4581.     GENTRY *ge2,
  4582.     double *max1,
  4583.     double *max2
  4584. )
  4585. {
  4586.     struct ray {
  4587.         double x1, y1, x2, y2;
  4588.         int isvert;
  4589.         double k, b; /* lines are represented as y = k*x + b */
  4590.         double *maxp;
  4591.     } ray [3];
  4592.     double x, y;
  4593.     int i;
  4594.  
  4595.     ray[0].x1 = ge1->prev->fx3;
  4596.     ray[0].y1 = ge1->prev->fy3;
  4597.     ray[0].x2 = ge1->fx1;
  4598.     ray[0].y2 = ge1->fy1;
  4599.     ray[0].maxp = max1;
  4600.  
  4601.     ray[1].x1 = ge2->fx3;
  4602.     ray[1].y1 = ge2->fy3;
  4603.     ray[1].x2 = ge2->fx2;
  4604.     ray[1].y2 = ge2->fy2;
  4605.     ray[1].maxp = max2;
  4606.  
  4607.     for(i=0; i<2; i++) {
  4608.         if(ray[i].x1 == ray[i].x2) 
  4609.             ray[i].isvert = 1;
  4610.         else {
  4611.             ray[i].isvert = 0;
  4612.             ray[i].k = (ray[i].y2 - ray[i].y1) / (ray[i].x2 - ray[i].x1);
  4613.             ray[i].b = ray[i].y2 - ray[i].k * ray[i].x2;
  4614.         }
  4615.     }
  4616.  
  4617.     if(ray[0].isvert && ray[1].isvert) {
  4618.         if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: both vertical\n");
  4619.         return 0; /* both vertical, don't cross */
  4620.     }
  4621.  
  4622.     if(ray[1].isvert) {
  4623.         ray[2] = ray[0]; /* exchange them */
  4624.         ray[0] = ray[1];
  4625.         ray[1] = ray[2];
  4626.     }
  4627.  
  4628.     if(ray[0].isvert) {
  4629.         x = ray[0].x1;
  4630.     } else {
  4631.         if( fabs(ray[0].k - ray[1].k) < FEPS) {
  4632.             if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: parallel lines, k = %g, %g\n",
  4633.                 ray[0].k, ray[1].k);
  4634.             return 0; /* parallel lines */
  4635.         }
  4636.         x = (ray[1].b - ray[0].b) / (ray[0].k - ray[1].k) ;
  4637.     }
  4638.     y = ray[1].k * x + ray[1].b;
  4639.  
  4640.     for(i=0; i<2; i++) {
  4641.         if(ray[i].isvert)
  4642.             *ray[i].maxp = (y - ray[i].y1) / (ray[i].y2 - ray[i].y1);
  4643.         else
  4644.             *ray[i].maxp = (x - ray[i].x1) / (ray[i].x2 - ray[i].x1);
  4645.         /* check if wrong sides of rays cross */
  4646.         if( *ray[i].maxp < 0 ) {
  4647.             if(ISDBG(FCONCISE)) fprintf(stderr, "crossrays: scale=%g @(%g,%g) (%g,%g)<-(%g,%g)\n",
  4648.                 *ray[i].maxp, x, y, ray[i].x2, ray[i].y2, ray[i].x1, ray[i].y1);
  4649.             return 0;
  4650.         }
  4651.     }
  4652.     return 1;
  4653. }
  4654.  
  4655. /* find the area covered by the curve
  4656.  * (limited by the projections to the X axis)
  4657.  */
  4658.  
  4659. static double
  4660. fcvarea(
  4661.     GENTRY *ge
  4662. )
  4663. {
  4664.     double Ly, My, Ny, Py, Qx, Rx, Sx;
  4665.     double area;
  4666.  
  4667.     /* y = Ly*t^3 + My*t^2 + Ny*t + Py */
  4668.     Ly = -ge->prev->fy3 + 3*(ge->fy1 - ge->fy2) + ge->fy3;
  4669.     My = 3*ge->prev->fy3 - 6*ge->fy1 + 3*ge->fy2;
  4670.     Ny = 3*(-ge->prev->fy3 + ge->fy1);
  4671.     Py = ge->prev->fy3;
  4672.  
  4673.     /* dx/dt = Qx*t^2 + Rx*t + Sx */
  4674.     Qx = 3*(-ge->prev->fx3 + 3*(ge->fx1 - ge->fx2) + ge->fx3);
  4675.     Rx = 6*(ge->prev->fx3 - 2*ge->fx1 + ge->fx2);
  4676.     Sx = 3*(-ge->prev->fx3 + ge->fx1);
  4677.  
  4678.     /* area is integral[from 0 to 1]( y(t) * dx(t)/dt *dt) */
  4679.     area = 1./6.*(Ly*Qx) + 1./5.*(Ly*Rx + My*Qx) 
  4680.         + 1./4.*(Ly*Sx + My*Rx + Ny*Qx) + 1./3.*(My*Sx + Ny*Rx + Py*Qx)
  4681.         + 1./2.*(Ny*Sx + Py*Rx) + Py*Sx;
  4682.  
  4683.     return area;
  4684. }
  4685.  
  4686. /* find the value of point on the curve at the given parameter t,
  4687.  * along the given axis (0 - X, 1 - Y).
  4688.  */
  4689.  
  4690. static double
  4691. fcvval(
  4692.     GENTRY *ge,
  4693.     int axis,
  4694.     double t
  4695. )
  4696. {
  4697.     double t2, mt, mt2;
  4698.  
  4699.     /* val = A*(1-t)^3 + 3*B*(1-t)^2*t + 3*C*(1-t)*t^2 + D*t^3 */
  4700.     t2 = t*t;
  4701.     mt = 1-t;
  4702.     mt2 = mt*mt;
  4703.     
  4704.     return ge->prev->fpoints[axis][2]*mt2*mt 
  4705.         + 3*(ge->fpoints[axis][0]*mt2*t + ge->fpoints[axis][1]*mt*t2)
  4706.         + ge->fpoints[axis][2]*t*t2;
  4707. }
  4708.  
  4709. /* Check that the new curve has the point identified by the 
  4710.  * parameter t reasonably close to the corresponding point
  4711.  * in the old pair of curves which were joined in proportion k.
  4712.  * If old2 is NULL then just compare nge and old1 at the point t.
  4713.  * Returns 0 if OK, 1 if it's too far.
  4714.  */
  4715.  
  4716. static int
  4717. fckjoinedcv(
  4718.     GLYPH *g,
  4719.     double t,
  4720.     GENTRY *nge,
  4721.     GENTRY *old1,
  4722.     GENTRY *old2,
  4723.     double k
  4724. )
  4725. {
  4726.     GENTRY *oge;
  4727.     double ot;
  4728.     double off;
  4729.     double lim;
  4730.     int i;
  4731.  
  4732.     if(old2 == 0) {
  4733.         oge = old1;
  4734.         ot = t;
  4735.     } else if(t <= k && k!=0.) {
  4736.         oge = old1;
  4737.         ot = t/k;
  4738.     } else {
  4739.         oge = old2;
  4740.         ot = (t-k) / (1.-k);
  4741.     }
  4742.  
  4743.     if(ISDBG(FCONCISE))
  4744.         fprintf(stderr, "%s: t=%g ot=%g (%x) ", g->name, t, ot, oge);
  4745.  
  4746.     for(i=0; i<2; i++) {
  4747.         /* permitted tolerance is 5% */
  4748.         lim = fabs(nge->fpoints[i][2] - nge->prev->fpoints[i][2])*0.05;
  4749.  
  4750.         if(lim < 3.)
  4751.             lim = 3.; /* for small curves the tolerance is higher */
  4752.         if(lim > 10.)
  4753.             lim = 10.; /* for big curves the tolerance is limited anyway */
  4754.  
  4755.         off = fabs(fcvval(nge, i, t) - fcvval(oge, i, ot));
  4756.  
  4757.         if(off > lim) {
  4758.             if(ISDBG(FCONCISE))
  4759.                 fprintf(stderr, "out of range d%c=%.2f(%.2f)\n", 
  4760.                     (i==0 ? 'X' : 'Y'), off, lim);
  4761.             return 1;
  4762.         }
  4763.  
  4764.         if(ISDBG(FCONCISE))
  4765.             fprintf(stderr, "valid d%c=%.2f(%.2f)  ", (i==0 ? 'X' : 'Y'), off, lim);
  4766.     }
  4767.     if(ISDBG(FCONCISE))
  4768.         fprintf(stderr, "\n");
  4769.     return 0;
  4770. }
  4771.  
  4772. /* force conciseness: substitute 2 or more curves going in the
  4773. ** same quadrant with one curve
  4774. ** in floating point
  4775. */
  4776.  
  4777. void
  4778. fforceconcise(
  4779.          GLYPH * g
  4780. )
  4781. {
  4782.     GENTRY         *ge, *nge;
  4783.     GENTRY          tge;
  4784.     double          firstlen, lastlen, sumlen, scale;
  4785.     double          dxw1, dyw1, dxw2, dyw2;
  4786.     double          dxb1, dyb1, dxe1, dye1;
  4787.     double          dxb2, dyb2, dxe2, dye2;
  4788.     double          maxsc1, maxsc2;
  4789.     int             i;
  4790.  
  4791.     assertisfloat(g, "enforcing conciseness");
  4792.  
  4793.     fdelsmall(g, 0.05);
  4794.     assertpath(g->entries, __FILE__, __LINE__, g->name);
  4795.     fnormalizec(g);
  4796.  
  4797.  
  4798.     for (ge = g->entries; ge != 0; ge = ge->next) {
  4799.         if (ge->type != GE_CURVE)
  4800.             continue;
  4801.  
  4802.         /* the whole direction of curve */
  4803.         dxw1 = ge->fx3 - ge->prev->fx3;
  4804.         dyw1 = ge->fy3 - ge->prev->fy3;
  4805.  
  4806.         while (1) {
  4807.             /* the whole direction of curve */
  4808.             dxw1 = ge->fx3 - ge->prev->fx3;
  4809.             dyw1 = ge->fy3 - ge->prev->fy3;
  4810.  
  4811.             /* directions of  ends of curve */
  4812.             dxb1 = ge->fx1 - ge->prev->fx3;
  4813.             dyb1 = ge->fy1 - ge->prev->fy3;
  4814.             dxe1 = ge->fx3 - ge->fx2;
  4815.             dye1 = ge->fy3 - ge->fy2;
  4816.  
  4817.             nge = ge->frwd;
  4818.  
  4819.             if (nge->type != GE_CURVE)
  4820.                 break;
  4821.  
  4822.             dxw2 = nge->fx3 - ge->fx3;
  4823.             dyw2 = nge->fy3 - ge->fy3;
  4824.  
  4825.             dxb2 = nge->fx1 - ge->fx3;
  4826.             dyb2 = nge->fy1 - ge->fy3;
  4827.             dxe2 = nge->fx3 - nge->fx2;
  4828.             dye2 = nge->fy3 - nge->fy2;
  4829.  
  4830.             /* if curve changes direction */
  4831.             if (fsign(dxw1) != fsign(dxw2) || fsign(dyw1) != fsign(dyw2))
  4832.                 break;
  4833.  
  4834.             /* if the arch is going in other direction */
  4835.             if (fsign(fabs(dxb1 * dyw1) - fabs(dyb1 * dxw1))
  4836.                 * fsign(fabs(dxe2 * dyw2) - fabs(dye2 * dxw2)) > 0)
  4837.                 break;
  4838.  
  4839.             /* get possible scale limits within which we won't cross quadrants */
  4840.             if( fcrossrays(ge, nge, &maxsc1, &maxsc2) == 0 ) {
  4841.                 if(ISDBG(FCONCISE)) {
  4842.                     fprintf(stderr, "glyph %s has curves with strange ends\n", g->name);
  4843.                     dumppaths(g, ge, nge);
  4844.                 }
  4845.                 break;
  4846.             }
  4847.  
  4848.             if(maxsc1 < 1. || maxsc2 < 1. ) /* would create a zigzag */
  4849.                 break;
  4850.  
  4851.             ge->dir = fgetcvdir(ge);
  4852.             nge->dir = fgetcvdir(nge);
  4853.  
  4854.             if( ((ge->dir&CVDIR_FRONT)-CVDIR_FEQUAL) * ((nge->dir&CVDIR_REAR)-CVDIR_REQUAL) < 0 )
  4855.                 /* would create a zigzag */
  4856.                 break;
  4857.  
  4858.             firstlen = sqrt( dxe1*dxe1 + dye1*dye1 );
  4859.             lastlen = sqrt( dxb2*dxb2 + dyb2*dyb2 );
  4860.             sumlen = firstlen + lastlen;
  4861.  
  4862.             /* check the scale limits */
  4863.             if( sumlen/firstlen > maxsc1 || sumlen/lastlen > maxsc2 ) {
  4864.                 if(ISDBG(FCONCISE)) 
  4865.                     fprintf(stderr, "%s: %x, %x would be crossing in forceconcise\n", 
  4866.                     g->name, ge, nge);
  4867.                 break;
  4868.             }
  4869.  
  4870.             /* OK, it seems like we can attempt to join these two curves */
  4871.             tge.flags = ge->flags;
  4872.             tge.prev = ge->prev;
  4873.             tge.fx1 = ge->fx1;
  4874.             tge.fy1 = ge->fy1;
  4875.             tge.fx2 = nge->fx2;
  4876.             tge.fy2 = nge->fy2;
  4877.             tge.fx3 = nge->fx3;
  4878.             tge.fy3 = nge->fy3;
  4879.  
  4880.             dxb1 = tge.fx1 - tge.prev->fx3;
  4881.             dyb1 = tge.fy1 - tge.prev->fy3;
  4882.             dxe1 = tge.fx3 - tge.fx2;
  4883.             dye1 = tge.fy3 - tge.fy2;
  4884.  
  4885.             /* scale the first segment */
  4886.             scale = sumlen / firstlen;
  4887.             tge.fx1 = tge.prev->fx3 + scale * dxb1;
  4888.             tge.fy1 = tge.prev->fy3 + scale * dyb1;
  4889.  
  4890.             /* scale the last segment */
  4891.             scale = sumlen / lastlen;
  4892.             tge.fx2 = tge.fx3 - scale * dxe1;
  4893.             tge.fy2 = tge.fy3 - scale * dye1;
  4894.  
  4895.             /* now check if we got something sensible */
  4896.  
  4897.             /* check if some important points is too far from original */
  4898.             scale = firstlen / sumlen;
  4899.             {
  4900.                 double pts[4] = { 0./*will be replaced*/, 0.5, 0.25, 0.75 };
  4901.                 int i, bad;
  4902.  
  4903.                 pts[0] = scale;
  4904.                 bad = 0;
  4905.  
  4906.                 for(i=0; i<sizeof(pts)/sizeof(pts[0]); i++)
  4907.                     if(fckjoinedcv(g, pts[i], &tge, ge, nge, scale)) {
  4908.                         bad = 1;
  4909.                         break;
  4910.                     }
  4911.                 if(bad)
  4912.                     break;
  4913.             }
  4914.  
  4915.             /* OK, it looks reasonably, let's apply it */
  4916.             if(ISDBG(FCONCISE)) 
  4917.                 dumppaths(g, ge, nge);
  4918.  
  4919.             for(i=0; i<3; i++) {
  4920.                 ge->fxn[i] = tge.fxn[i];
  4921.                 ge->fyn[i] = tge.fyn[i];
  4922.             }
  4923.  
  4924.             freethisge(nge);
  4925.         }
  4926.     }
  4927. }
  4928.  
  4929. void
  4930. print_glyph(
  4931.        int glyphno
  4932. )
  4933. {
  4934.     GLYPH          *g;
  4935.     GENTRY         *ge;
  4936.     int             x = 0, y = 0;
  4937.     int             i;
  4938.     int             grp, lastgrp= -1;
  4939.  
  4940.     g = &glyph_list[glyphno];
  4941.  
  4942.     fprintf(pfa_file, "/%s { \n", g->name);
  4943.  
  4944.     /* consider widths >MAXLEGALWIDTH as bugs */
  4945.     if( g->scaledwidth <= MAXLEGALWIDTH ) {
  4946.         fprintf(pfa_file, "0 %d hsbw\n", g->scaledwidth);
  4947.     } else {
  4948.         fprintf(pfa_file, "0 1000 hsbw\n");
  4949.         WARNING_2 fprintf(stderr, "glyph %s: width %d seems to be buggy, set to 1000\n",
  4950.             g->name, g->scaledwidth);
  4951.     }
  4952.  
  4953. #if 0
  4954.     fprintf(pfa_file, "%% contours: ");
  4955.     for (i = 0; i < g->ncontours; i++)
  4956.         fprintf(pfa_file, "%s(%d,%d) ", (g->contours[i].direction == DIR_OUTER ? "out" : "in"),
  4957.             g->contours[i].xofmin, g->contours[i].ymin);
  4958.     fprintf(pfa_file, "\n");
  4959.  
  4960.     if (g->rymin < 5000)
  4961.         fprintf(pfa_file, "%d lower%s\n", g->rymin, (g->flatymin ? "flat" : "curve"));
  4962.     if (g->rymax > -5000)
  4963.         fprintf(pfa_file, "%d upper%s\n", g->rymax, (g->flatymax ? "flat" : "curve"));
  4964. #endif
  4965.  
  4966.     if (g->hstems)
  4967.         for (i = 0; i < g->nhs; i += 2) {
  4968.             if (g->hstems[i].flags & ST_3) {
  4969.                 fprintf(pfa_file, "%d %d %d %d %d %d hstem3\n",
  4970.                     g->hstems[i].value,
  4971.                 g->hstems[i + 1].value - g->hstems[i].value,
  4972.                     g->hstems[i + 2].value,
  4973.                     g->hstems[i + 3].value - g->hstems[i + 2].value,
  4974.                     g->hstems[i + 4].value,
  4975.                     g->hstems[i + 5].value - g->hstems[i + 4].value
  4976.                     );
  4977.                 i += 4;
  4978.             } else {
  4979.                 fprintf(pfa_file, "%d %d hstem\n", g->hstems[i].value,
  4980.                 g->hstems[i + 1].value - g->hstems[i].value);
  4981.             }
  4982.         }
  4983.  
  4984.     if (g->vstems)
  4985.         for (i = 0; i < g->nvs; i += 2) {
  4986.             if (g->vstems[i].flags & ST_3) {
  4987.                 fprintf(pfa_file, "%d %d %d %d %d %d vstem3\n",
  4988.                     g->vstems[i].value,
  4989.                 g->vstems[i + 1].value - g->vstems[i].value,
  4990.                     g->vstems[i + 2].value,
  4991.                     g->vstems[i + 3].value - g->vstems[i + 2].value,
  4992.                     g->vstems[i + 4].value,
  4993.                     g->vstems[i + 5].value - g->vstems[i + 4].value
  4994.                     );
  4995.                 i += 4;
  4996.             } else {
  4997.                 fprintf(pfa_file, "%d %d vstem\n", g->vstems[i].value,
  4998.                 g->vstems[i + 1].value - g->vstems[i].value);
  4999.             }
  5000.         }
  5001.  
  5002.     for (ge = g->entries; ge != 0; ge = ge->next) {
  5003.         if(g->nsg>0) {
  5004.             grp=ge->stemid;
  5005.             if(grp >= 0 && grp != lastgrp)  {
  5006.                 fprintf(pfa_file, "%d 4 callsubr\n", grp+g->firstsubr);
  5007.                 lastgrp=grp;
  5008.             }
  5009.         }
  5010.  
  5011.         switch (ge->type) {
  5012.         case GE_MOVE:
  5013.             if (absolute)
  5014.                 fprintf(pfa_file, "%d %d amoveto\n", ge->ix3, ge->iy3);
  5015.             else
  5016.                 rmoveto(ge->ix3 - x, ge->iy3 - y);
  5017.             if (0)
  5018.                 fprintf(stderr, "Glyph %s: print moveto(%d, %d)\n",
  5019.                     g->name, ge->ix3, ge->iy3);
  5020.             x = ge->ix3;
  5021.             y = ge->iy3;
  5022.             break;
  5023.         case GE_LINE:
  5024.             if (absolute)
  5025.                 fprintf(pfa_file, "%d %d alineto\n", ge->ix3, ge->iy3);
  5026.             else
  5027.                 rlineto(ge->ix3 - x, ge->iy3 - y);
  5028.             x = ge->ix3;
  5029.             y = ge->iy3;
  5030.             break;
  5031.         case GE_CURVE:
  5032.             if (absolute)
  5033.                 fprintf(pfa_file, "%d %d %d %d %d %d arcurveto\n",
  5034.                     ge->ix1, ge->iy1, ge->ix2, ge->iy2, ge->ix3, ge->iy3);
  5035.             else
  5036.                 rrcurveto(ge->ix1 - x, ge->iy1 - y,
  5037.                       ge->ix2 - ge->ix1, ge->iy2 - ge->iy1,
  5038.                       ge->ix3 - ge->ix2, ge->iy3 - ge->iy2);
  5039.             x = ge->ix3;
  5040.             y = ge->iy3;
  5041.             break;
  5042.         case GE_PATH:
  5043.             closepath();
  5044.             break;
  5045.         default:
  5046.             WARNING_1 fprintf(stderr, "**** Glyph %s: unknown entry type '%c'\n",
  5047.                 g->name, ge->type);
  5048.             break;
  5049.         }
  5050.     }
  5051.  
  5052.     fprintf(pfa_file, "endchar } ND\n");
  5053. }
  5054.  
  5055. /* print the subroutines for this glyph, returns the number of them */
  5056. int
  5057. print_glyph_subs(
  5058.        int glyphno,
  5059.        int startid /* start numbering subroutines from this id */
  5060. )
  5061. {
  5062.     GLYPH *g;
  5063.     int i, grp;
  5064.  
  5065.     g = &glyph_list[glyphno];
  5066.  
  5067.     if(!hints || !subhints || g->nsg<1)
  5068.         return 0;
  5069.  
  5070.     g->firstsubr=startid;
  5071.  
  5072. #if 0
  5073.     fprintf(pfa_file, "%% %s %d\n", g->name, g->nsg);
  5074. #endif
  5075.     for(grp=0; grp<g->nsg; grp++) {
  5076.         fprintf(pfa_file, "dup %d {\n", startid++);
  5077.         for(i= (grp==0)? 0 : g->nsbs[grp-1]; i<g->nsbs[grp]; i++)
  5078.             fprintf(pfa_file, "\t%d %d %cstem\n", g->sbstems[i].low, 
  5079.                 g->sbstems[i].high-g->sbstems[i].low,
  5080.                 g->sbstems[i].isvert ? 'v' : 'h');
  5081.         fprintf(pfa_file, "\treturn\n\t} NP\n");
  5082.     }
  5083.  
  5084.     return g->nsg;
  5085. }
  5086.  
  5087. void
  5088. print_glyph_metrics(
  5089.        int code,
  5090.        int glyphno
  5091. )
  5092. {
  5093.     GLYPH *g;
  5094.  
  5095.     g = &glyph_list[glyphno];
  5096.  
  5097.     if(transform)
  5098.       fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
  5099.           code, g->scaledwidth, g->name,
  5100.           iscale(g->xMin), iscale(g->yMin), iscale(g->xMax), iscale(g->yMax));
  5101.     else
  5102.       fprintf(afm_file, "C %d ; WX %d ; N %s ; B %d %d %d %d ;\n",
  5103.           code, g->scaledwidth, g->name,
  5104.           g->xMin, g->yMin, g->xMax, g->yMax);
  5105. }
  5106.  
  5107. /*
  5108.  SB:
  5109.  An important note about the BlueValues.
  5110.  
  5111.  The Adobe documentation says that the maximal width of a Blue zone
  5112.  is connected to the value of BlueScale, which is by default 0.039625.
  5113.  The BlueScale value defines, at which point size the overshoot
  5114.  suppression be disabled.
  5115.  
  5116.  The formula for it that is given in the manual is:
  5117.  
  5118.   BlueScale=point_size/240, for a 300dpi device
  5119.  
  5120.  that makes us wonder what is this 240 standing for. Incidentally
  5121.  240=72*1000/300, where 72 is the relation between inches and points,
  5122.  1000 is the size of the glyph matrix, and 300dpi is the resolution of
  5123.  the output device. Knowing that we can recalculate the formula for
  5124.  the font size in pixels rather than points:
  5125.  
  5126.   BlueScale=pixel_size/1000
  5127.  
  5128.  That looks a lot simpler than the original formula, does not it ?
  5129.  And the limitation about the maximal width of zone also looks
  5130.  a lot simpler after the transformation:
  5131.  
  5132.   max_width < 1000/pixel_size
  5133.  
  5134.  that ensures that even at the maximal pixel size when the overshoot
  5135.  suppression is disabled the zone width will be less than one pixel.
  5136.  This is important, failure to comply to this limit will result in
  5137.  really ugly fonts (been there, done that). But knowing the formula
  5138.  for the pixel width, we see that in fact we can use the maximal width
  5139.  of 24, not 23 as specified in the manual.
  5140.  
  5141. */
  5142.  
  5143. #define MAXBLUEWIDTH (24)
  5144.  
  5145. /*
  5146.  * Find the indexes of the most frequent values
  5147.  * in the hystogram, sort them in ascending order, and save which one
  5148.  * was the best one (if asked).
  5149.  * Returns the number of values found (may be less than maximal because
  5150.  * we ignore the zero values)
  5151.  */
  5152.  
  5153. #define MAXHYST    (2000)        /* size of the hystogram */
  5154. #define HYSTBASE 500
  5155.  
  5156. static int
  5157. besthyst(
  5158.      int *hyst,        /* the hystogram */
  5159.      int base,        /* the base point of the hystogram */
  5160.      int *best,        /* the array for indexes of best values */
  5161.      int nbest,        /* its allocated size */
  5162.      int width,        /* minimal difference between indexes */
  5163.      int *bestindp        /* returned top point */
  5164. )
  5165. {
  5166.     unsigned char   hused[MAXHYST / 8 + 1];
  5167.     int             i, max, j, w, last = 0;
  5168.     int             nf = 0;
  5169.  
  5170.     width--;
  5171.  
  5172.     memset(hused, 0 , sizeof hused);
  5173.  
  5174.     max = 1;
  5175.     for (i = 0; i < nbest && max != 0; i++) {
  5176.         best[i] = 0;
  5177.         max = 0;
  5178.         for (j = 1; j < MAXHYST - 1; j++) {
  5179.             w = hyst[j];
  5180.  
  5181.             if (w > max && (hused[j>>3] & (1 << (j & 0x07))) == 0) {
  5182.                 best[i] = j;
  5183.                 max = w;
  5184.             }
  5185.         }
  5186.         if (max != 0) {
  5187.             if (max < last/2) {
  5188.                 /* do not pick the too low values */
  5189.                 break;
  5190.             }
  5191.             for (j = best[i] - width; j <= best[i] + width; j++) {
  5192.                 if (j >= 0 && j < MAXHYST)
  5193.                     hused[j >> 3] |= (1 << (j & 0x07));
  5194.             }
  5195.             last = max;
  5196.             best[i] -= base;
  5197.             nf = i + 1;
  5198.         }
  5199.     }
  5200.  
  5201.     if (bestindp)
  5202.         *bestindp = best[0];
  5203.  
  5204.     /* sort the indexes in ascending order */
  5205.     for (i = 0; i < nf; i++) {
  5206.         for (j = i + 1; j < nf; j++)
  5207.             if (best[j] < best[i]) {
  5208.                 w = best[i];
  5209.                 best[i] = best[j];
  5210.                 best[j] = w;
  5211.             }
  5212.     }
  5213.  
  5214.     return nf;
  5215. }
  5216.  
  5217. /*
  5218.  * Find the next best Blue zone in the hystogram.
  5219.  * Return the weight of the found zone.
  5220.  */
  5221.  
  5222. static int
  5223. bestblue(
  5224.      short *zhyst,        /* the zones hystogram */
  5225.      short *physt,        /* the points hystogram */
  5226.      short *ozhyst,        /* the other zones hystogram */
  5227.      int *bluetab        /* where to put the found zone */
  5228. )
  5229. {
  5230.     int             i, j, w, max, ind, first, last;
  5231.  
  5232.     /* find the highest point in the zones hystogram */
  5233.     /* if we have a plateau, take its center */
  5234.     /* if we have multiple peaks, take the first one */
  5235.  
  5236.     max = -1;
  5237.     first = last = -10;
  5238.     for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
  5239.         w = zhyst[i];
  5240.         if (w > max) {
  5241.             first = last = i;
  5242.             max = w;
  5243.         } else if (w == max) {
  5244.             if (last == i - 1)
  5245.                 last = i;
  5246.         }
  5247.     }
  5248.     ind = (first + last) / 2;
  5249.  
  5250.     if (max == 0)        /* no zones left */
  5251.         return 0;
  5252.  
  5253.     /* now we reuse `first' and `last' as inclusive borders of the zone */
  5254.     first = ind;
  5255.     last = ind + (MAXBLUEWIDTH - 1);
  5256.  
  5257.     /* our maximal width is far too big, so we try to make it narrower */
  5258.     w = max;
  5259.     j = (w & 1);        /* a pseudo-random bit */
  5260.     while (1) {
  5261.         while (physt[first] == 0)
  5262.             first++;
  5263.         while (physt[last] == 0)
  5264.             last--;
  5265.         if (last - first < (MAXBLUEWIDTH * 2 / 3) || (max - w) * 10 > max)
  5266.             break;
  5267.  
  5268.         if (physt[first] < physt[last]
  5269.             || physt[first] == physt[last] && j) {
  5270.             if (physt[first] * 20 > w)    /* if weight is >5%,
  5271.                              * stop */
  5272.                 break;
  5273.             w -= physt[first];
  5274.             first++;
  5275.             j = 0;
  5276.         } else {
  5277.             if (physt[last] * 20 > w)    /* if weight is >5%,
  5278.                              * stop */
  5279.                 break;
  5280.             w -= physt[last];
  5281.             last--;
  5282.             j = 1;
  5283.         }
  5284.     }
  5285.  
  5286.     /* save our zone */
  5287.     bluetab[0] = first - HYSTBASE;
  5288.     bluetab[1] = last - HYSTBASE;
  5289.  
  5290.     /* invalidate all the zones overlapping with this one */
  5291.     /* the constant of 2 is determined by the default value of BlueFuzz */
  5292.     for (i = first - (MAXBLUEWIDTH - 1) - 2; i <= last + 2; i++)
  5293.         if (i >= 0 && i < MAXHYST) {
  5294.             zhyst[i] = 0;
  5295.             ozhyst[i] = 0;
  5296.         }
  5297.     return w;
  5298. }
  5299.  
  5300. /*
  5301.  * Try to find the Blue Values, bounding box and italic angle
  5302.  */
  5303.  
  5304. void
  5305. findblues(void)
  5306. {
  5307.     /* hystograms for upper and lower zones */
  5308.     short           hystl[MAXHYST];
  5309.     short           hystu[MAXHYST];
  5310.     short           zuhyst[MAXHYST];
  5311.     short           zlhyst[MAXHYST];
  5312.     int             nchars;
  5313.     int             i, j, k, w, max;
  5314.     GENTRY         *ge;
  5315.     GLYPH          *g;
  5316.     double          ang;
  5317.  
  5318.     /* find the lowest and highest points of glyphs */
  5319.     /* and by the way build the values for FontBBox */
  5320.     /* and build the hystogram for the ItalicAngle */
  5321.  
  5322.     /* re-use hystl for the hystogram of italic angle */
  5323.  
  5324.     bbox[0] = bbox[1] = 5000;
  5325.     bbox[2] = bbox[3] = -5000;
  5326.  
  5327.     for (i = 0; i < MAXHYST; i++)
  5328.         hystl[i] = 0;
  5329.  
  5330.     nchars = 0;
  5331.  
  5332.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5333.         if (g->flags & GF_USED) {
  5334.             nchars++;
  5335.  
  5336.             g->rymin = 5000;
  5337.             g->rymax = -5000;
  5338.             for (ge = g->entries; ge != 0; ge = ge->next) {
  5339.                 if (ge->type == GE_LINE) {
  5340.  
  5341.                     j = ge->iy3 - ge->prev->iy3;
  5342.                     k = ge->ix3 - ge->prev->ix3;
  5343.                     if (j > 0)
  5344.                         ang = atan2(-k, j) * 180.0 / M_PI;
  5345.                     else
  5346.                         ang = atan2(k, -j) * 180.0 / M_PI;
  5347.  
  5348.                     k /= 100;
  5349.                     j /= 100;
  5350.                     if (ang > -45.0 && ang < 45.0) {
  5351.                         /*
  5352.                          * be careful to not overflow
  5353.                          * the counter
  5354.                          */
  5355.                         hystl[HYSTBASE + (int) (ang * 10.0)] += (k * k + j * j) / 4;
  5356.                     }
  5357.                     if (ge->iy3 == ge->prev->iy3) {
  5358.                         if (ge->iy3 <= g->rymin) {
  5359.                             g->rymin = ge->iy3;
  5360.                             g->flatymin = 1;
  5361.                         }
  5362.                         if (ge->iy3 >= g->rymax) {
  5363.                             g->rymax = ge->iy3;
  5364.                             g->flatymax = 1;
  5365.                         }
  5366.                     } else {
  5367.                         if (ge->iy3 < g->rymin) {
  5368.                             g->rymin = ge->iy3;
  5369.                             g->flatymin = 0;
  5370.                         }
  5371.                         if (ge->iy3 > g->rymax) {
  5372.                             g->rymax = ge->iy3;
  5373.                             g->flatymax = 0;
  5374.                         }
  5375.                     }
  5376.                 } else if (ge->type == GE_CURVE) {
  5377.                     if (ge->iy3 < g->rymin) {
  5378.                         g->rymin = ge->iy3;
  5379.                         g->flatymin = 0;
  5380.                     }
  5381.                     if (ge->iy3 > g->rymax) {
  5382.                         g->rymax = ge->iy3;
  5383.                         g->flatymax = 0;
  5384.                     }
  5385.                 }
  5386.                 if (ge->type == GE_LINE || ge->type == GE_CURVE) {
  5387.                     if (ge->ix3 < bbox[0])
  5388.                         bbox[0] = ge->ix3;
  5389.                     if (ge->ix3 > bbox[2])
  5390.                         bbox[2] = ge->ix3;
  5391.                     if (ge->iy3 < bbox[1])
  5392.                         bbox[1] = ge->iy3;
  5393.                     if (ge->iy3 > bbox[3])
  5394.                         bbox[3] = ge->iy3;
  5395.                 }
  5396.             }
  5397.         }
  5398.     }
  5399.  
  5400.     /* get the most popular angle */
  5401.     max = 0;
  5402.     w = 0;
  5403.     for (i = 0; i < MAXHYST; i++) {
  5404.         if (hystl[i] > w) {
  5405.             w = hystl[i];
  5406.             max = i;
  5407.         }
  5408.     }
  5409.     ang = (double) (max - HYSTBASE) / 10.0;
  5410.     WARNING_2 fprintf(stderr, "Guessed italic angle: %f\n", ang);
  5411.     if (italic_angle == 0.0)
  5412.         italic_angle = ang;
  5413.  
  5414.     /* build the hystogram of the lower points */
  5415.     for (i = 0; i < MAXHYST; i++)
  5416.         hystl[i] = 0;
  5417.  
  5418.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5419.         if ((g->flags & GF_USED)
  5420.             && g->rymin + HYSTBASE >= 0 && g->rymin < MAXHYST - HYSTBASE) {
  5421.             hystl[g->rymin + HYSTBASE]++;
  5422.         }
  5423.     }
  5424.  
  5425.     /* build the hystogram of the upper points */
  5426.     for (i = 0; i < MAXHYST; i++)
  5427.         hystu[i] = 0;
  5428.  
  5429.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5430.         if ((g->flags & GF_USED)
  5431.             && g->rymax + HYSTBASE >= 0 && g->rymax < MAXHYST - HYSTBASE) {
  5432.             hystu[g->rymax + HYSTBASE]++;
  5433.         }
  5434.     }
  5435.  
  5436.     /* build the hystogram of all the possible lower zones with max width */
  5437.     for (i = 0; i < MAXHYST; i++)
  5438.         zlhyst[i] = 0;
  5439.  
  5440.     for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
  5441.         for (j = 0; j < MAXBLUEWIDTH; j++)
  5442.             zlhyst[i] += hystl[i + j];
  5443.     }
  5444.  
  5445.     /* build the hystogram of all the possible upper zones with max width */
  5446.     for (i = 0; i < MAXHYST; i++)
  5447.         zuhyst[i] = 0;
  5448.  
  5449.     for (i = 0; i <= MAXHYST - MAXBLUEWIDTH; i++) {
  5450.         for (j = 0; j < MAXBLUEWIDTH; j++)
  5451.             zuhyst[i] += hystu[i + j];
  5452.     }
  5453.  
  5454.     /* find the baseline */
  5455.     w = bestblue(zlhyst, hystl, zuhyst, &bluevalues[0]);
  5456.     if (0)
  5457.         fprintf(stderr, "BaselineBlue zone %d%% %d...%d\n", w * 100 / nchars,
  5458.                 bluevalues[0], bluevalues[1]);
  5459.  
  5460.     if (w == 0)        /* no baseline, something weird */
  5461.         return;
  5462.  
  5463.     /* find the upper zones */
  5464.     for (nblues = 2; nblues < 14; nblues += 2) {
  5465.         w = bestblue(zuhyst, hystu, zlhyst, &bluevalues[nblues]);
  5466.  
  5467.         if (0)
  5468.             fprintf(stderr, "Blue zone %d%% %d...%d\n", w * 100 / nchars, 
  5469.                 bluevalues[nblues], bluevalues[nblues+1]);
  5470.  
  5471.         if (w * 20 < nchars)
  5472.             break;    /* don't save this zone */
  5473.     }
  5474.  
  5475.     /* find the lower zones */
  5476.     for (notherb = 0; notherb < 10; notherb += 2) {
  5477.         w = bestblue(zlhyst, hystl, zuhyst, &otherblues[notherb]);
  5478.  
  5479.         if (0)
  5480.             fprintf(stderr, "OtherBlue zone %d%% %d...%d\n", w * 100 / nchars,
  5481.                 otherblues[notherb], otherblues[notherb+1]);
  5482.  
  5483.  
  5484.         if (w * 20 < nchars)
  5485.             break;    /* don't save this zone */
  5486.     }
  5487.  
  5488. }
  5489.  
  5490. /*
  5491.  * Find the actual width of the glyph and modify the
  5492.  * description to reflect it. Not guaranteed to do
  5493.  * any good, may make character spacing too wide.
  5494.  */
  5495.  
  5496. void
  5497. docorrectwidth(void)
  5498. {
  5499.     int             i;
  5500.     GENTRY         *ge;
  5501.     GLYPH          *g;
  5502.     int             xmin, xmax;
  5503.     int             maxwidth, minsp;
  5504.  
  5505.     /* enforce this minimal spacing,
  5506.      * we limit the amount of the enforced spacing to avoid
  5507.      * spacing the bold wonts too widely
  5508.      */
  5509.     minsp = (stdhw>60 || stdhw<10)? 60 : stdhw;
  5510.  
  5511.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5512.         g->oldwidth=g->scaledwidth; /* save the old width, will need for AFM */
  5513.  
  5514.         if (correctwidth && g->flags & GF_USED) {
  5515.             xmin = 5000;
  5516.             xmax = -5000;
  5517.             for (ge = g->entries; ge != 0; ge = ge->next) {
  5518.                 if (ge->type != GE_LINE && ge->type != GE_CURVE) 
  5519.                     continue;
  5520.  
  5521.                 if (ge->ix3 <= xmin) {
  5522.                     xmin = ge->ix3;
  5523.                 }
  5524.                 if (ge->ix3 >= xmax) {
  5525.                     xmax = ge->ix3;
  5526.                 }
  5527.             }
  5528.  
  5529.             maxwidth=xmax+minsp;
  5530.             if( g->scaledwidth < maxwidth ) {
  5531.                 g->scaledwidth = maxwidth;
  5532.                 WARNING_3 fprintf(stderr, "glyph %s: extended from %d to %d\n",
  5533.                     g->name, g->oldwidth, g->scaledwidth );
  5534.             }
  5535.         }
  5536.     }
  5537.  
  5538. }
  5539.  
  5540. /*
  5541.  * Try to find the typical stem widths
  5542.  */
  5543.  
  5544. void
  5545. stemstatistics(void)
  5546. {
  5547. #define MINDIST    10 /* minimal distance between the widths */
  5548.     int             hyst[MAXHYST+MINDIST*2];
  5549.     int             best[12];
  5550.     int             i, j, k, w;
  5551.     int             nchars;
  5552.     int             ns;
  5553.     STEM           *s;
  5554.     GLYPH          *g;
  5555.  
  5556.     /* start with typical stem width */
  5557.  
  5558.     nchars=0;
  5559.  
  5560.     /* build the hystogram of horizontal stem widths */
  5561.     memset(hyst, 0, sizeof hyst);
  5562.  
  5563.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5564.         if (g->flags & GF_USED) {
  5565.             nchars++;
  5566.             s = g->hstems;
  5567.             for (j = 0; j < g->nhs; j += 2) {
  5568.                 if ((s[j].flags | s[j + 1].flags) & ST_END)
  5569.                     continue;
  5570.                 w = s[j + 1].value - s[j].value+1;
  5571.                 if(w==20) /* split stems should not be counted */
  5572.                     continue;
  5573.                 if (w > 0 && w < MAXHYST - 1) {
  5574.                     /*
  5575.                      * handle some fuzz present in
  5576.                      * converted fonts
  5577.                      */
  5578.                     hyst[w+MINDIST] += MINDIST-1;
  5579.                     for(k=1; k<MINDIST-1; k++) {
  5580.                         hyst[w+MINDIST + k] += MINDIST-1-k;
  5581.                         hyst[w+MINDIST - k] += MINDIST-1-k;
  5582.                     }
  5583.                 }
  5584.             }
  5585.         }
  5586.     }
  5587.  
  5588.     /* find 12 most frequent values */
  5589.     ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdhw);
  5590.  
  5591.     /* store data in stemsnaph */
  5592.     for (i = 0; i < ns; i++)
  5593.         stemsnaph[i] = best[i];
  5594.     if (ns < 12)
  5595.         stemsnaph[ns] = 0;
  5596.  
  5597.     /* build the hystogram of vertical stem widths */
  5598.     memset(hyst, 0, sizeof hyst);
  5599.  
  5600.     for (i = 0, g = glyph_list; i < numglyphs; i++, g++) {
  5601.         if (g->flags & GF_USED) {
  5602.             s = g->vstems;
  5603.             for (j = 0; j < g->nvs; j += 2) {
  5604.                 if ((s[j].flags | s[j + 1].flags) & ST_END)
  5605.                     continue;
  5606.                 w = s[j + 1].value - s[j].value+1;
  5607.                 if (w > 0 && w < MAXHYST - 1) {
  5608.                     /*
  5609.                      * handle some fuzz present in
  5610.                      * converted fonts
  5611.                      */
  5612.                     hyst[w+MINDIST] += MINDIST-1;
  5613.                     for(k=1; k<MINDIST-1; k++) {
  5614.                         hyst[w+MINDIST + k] += MINDIST-1-k;
  5615.                         hyst[w+MINDIST - k] += MINDIST-1-k;
  5616.                     }
  5617.                 }
  5618.             }
  5619.         }
  5620.     }
  5621.  
  5622.     /* find 12 most frequent values */
  5623.     ns = besthyst(hyst+MINDIST, 0, best, 12, MINDIST, &stdvw);
  5624.  
  5625.     /* store data in stemsnaph */
  5626.     for (i = 0; i < ns; i++)
  5627.         stemsnapv[i] = best[i];
  5628.     if (ns < 12)
  5629.         stemsnapv[ns] = 0;
  5630.  
  5631. #undef MINDIST
  5632. }
  5633.  
  5634. /*
  5635.  * SB
  5636.  * A funny thing: TTF paths are going in reverse direction compared
  5637.  * to Type1. So after all (because the rest of logic uses TTF
  5638.  * path directions) we have to reverse the paths.
  5639.  *
  5640.  * It was a big headache to discover that.
  5641.  */
  5642.  
  5643. /* works on both int and float paths */
  5644.  
  5645. void
  5646. reversepathsfromto(
  5647.            GENTRY * from,
  5648.            GENTRY * to
  5649. )
  5650. {
  5651.     GENTRY         *ge, *nge, *pge;
  5652.     GENTRY         *cur, *next;
  5653.     int i, n, ilast[2];
  5654.     double flast[2], f;
  5655.  
  5656.     for (ge = from; ge != 0 && ge != to; ge = ge->next) {
  5657.         if(ge->type == GE_LINE || ge->type == GE_CURVE) {
  5658.             if (ISDBG(REVERSAL))
  5659.                 fprintf(stderr, "reverse path 0x%x <- 0x%x, 0x%x\n", ge, ge->prev, ge->bkwd);
  5660.  
  5661.             /* cut out the path itself */
  5662.             pge = ge->prev; /* GE_MOVE */
  5663.             if (pge == 0) {
  5664.                 fprintf(stderr, "**! No MOVE before line !!! Fatal. ****\n");
  5665.                 exit(1);
  5666.             }
  5667.             nge = ge->bkwd->next; /* GE_PATH */
  5668.             pge->next = nge;
  5669.             nge->prev = pge;
  5670.             ge->bkwd->next = 0; /* mark end of chain */
  5671.  
  5672.             /* remember the starting point */
  5673.             if(ge->flags & GEF_FLOAT) {
  5674.                 flast[0] = pge->fx3;
  5675.                 flast[1] = pge->fy3;
  5676.             } else {
  5677.                 ilast[0] = pge->ix3;
  5678.                 ilast[1] = pge->iy3;
  5679.             }
  5680.  
  5681.             /* then reinsert them in backwards order */
  5682.             for(cur = ge; cur != 0; cur = next ) {
  5683.                 next = cur->next; /* or addgeafter() will screw it up */
  5684.                 if(cur->flags & GEF_FLOAT) {
  5685.                     for(i=0; i<2; i++) {
  5686.                         /* reverse the direction of path element */
  5687.                         f = cur->fpoints[i][0];
  5688.                         cur->fpoints[i][0] = cur->fpoints[i][1];
  5689.                         cur->fpoints[i][1] = f;
  5690.                         f = flast[i];
  5691.                         flast[i] = cur->fpoints[i][2];
  5692.                         cur->fpoints[i][2] = f;
  5693.                     }
  5694.                 } else {
  5695.                     for(i=0; i<2; i++) {
  5696.                         /* reverse the direction of path element */
  5697.                         n = cur->ipoints[i][0];
  5698.                         cur->ipoints[i][0] = cur->ipoints[i][1];
  5699.                         cur->ipoints[i][1] = n;
  5700.                         n = ilast[i];
  5701.                         ilast[i] = cur->ipoints[i][2];
  5702.                         cur->ipoints[i][2] = n;
  5703.                     }
  5704.                 }
  5705.                 addgeafter(pge, cur);
  5706.             }
  5707.  
  5708.             /* restore the starting point */
  5709.             if(ge->flags & GEF_FLOAT) {
  5710.                 pge->fx3 = flast[0];
  5711.                 pge->fy3 = flast[1];
  5712.             } else {
  5713.                 pge->ix3 = ilast[0];
  5714.                 pge->iy3 = ilast[1];
  5715.             }
  5716.  
  5717.             ge = nge;
  5718.         }
  5719.  
  5720.     }
  5721. }
  5722.  
  5723. void
  5724. reversepaths(
  5725.          GLYPH * g
  5726. )
  5727. {
  5728.     reversepathsfromto(g->entries, NULL);
  5729. }
  5730.  
  5731. /* add a kerning pair information, scales the value */
  5732.  
  5733. void
  5734. addkernpair(
  5735.     unsigned id1,
  5736.     unsigned id2,
  5737.     int unscval
  5738. )
  5739. {
  5740.     static unsigned char *bits = 0;
  5741.     static int lastid;
  5742.     GLYPH *g = &glyph_list[id1];
  5743.     int i, n;
  5744.     struct kern *p;
  5745.  
  5746.     if(unscval == 0 || id1 >= numglyphs || id2 >= numglyphs)
  5747.         return;
  5748.  
  5749.     if( (glyph_list[id1].flags & GF_USED)==0
  5750.     || (glyph_list[id2].flags & GF_USED)==0 )
  5751.         return;
  5752.  
  5753.     if(bits == 0) {
  5754.         bits = calloc( BITMAP_BYTES(numglyphs), 1);
  5755.         if (bits == NULL) {
  5756.             fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
  5757.             exit(255);
  5758.         }
  5759.         lastid = id1;
  5760.     }
  5761.  
  5762.     if(lastid != id1) {
  5763.         /* refill the bitmap cache */
  5764.         memset(bits, 0,BITMAP_BYTES(numglyphs));
  5765.         p = g->kern;
  5766.         for(i=g->kerncount; i>0; i--) {
  5767.             n = (p++)->id;
  5768.             SET_BITMAP(bits, n);
  5769.         }
  5770.         lastid = id1;
  5771.     }
  5772.  
  5773.     if(IS_BITMAP(bits, id2))
  5774.         return; /* duplicate */
  5775.  
  5776.     if(g->kerncount <= g->kernalloc) {
  5777.         g->kernalloc += 8;
  5778.         p = realloc(g->kern, sizeof(struct kern) * g->kernalloc);
  5779.         if(p == 0) {
  5780.             fprintf (stderr, "** realloc failed, kerning data will be incomplete\n");
  5781.         }
  5782.         g->kern = p;
  5783.     }
  5784.  
  5785.     SET_BITMAP(bits, id2);
  5786.     p = &g->kern[g->kerncount];
  5787.     p->id = id2;
  5788.     p->val = iscale(unscval) - (g->scaledwidth - g->oldwidth);
  5789.     g->kerncount++;
  5790.     kerning_pairs++;
  5791. }
  5792.  
  5793. /* print out the kerning information */
  5794.  
  5795. void
  5796. print_kerning(
  5797.     FILE *afm_file
  5798. )
  5799. {
  5800.     int    i, j, n;
  5801.     GLYPH *g;
  5802.     struct kern *p;
  5803.  
  5804.     if( kerning_pairs == 0 ) 
  5805.         return;
  5806.  
  5807.     fprintf(afm_file, "StartKernData\n");
  5808.     fprintf(afm_file, "StartKernPairs %hd\n", kerning_pairs);
  5809.  
  5810.     for(i=0; i<numglyphs; i++)  {
  5811.         g = &glyph_list[i];
  5812.         if( (g->flags & GF_USED) ==0)
  5813.             continue;
  5814.         p = g->kern;
  5815.         for(j=g->kerncount; j>0; j--, p++) {
  5816.             fprintf(afm_file, "KPX %s %s %d\n", g->name, 
  5817.                 glyph_list[ p->id ].name, p->val );
  5818.         }
  5819.     }
  5820.  
  5821.     fprintf(afm_file, "EndKernPairs\n");
  5822.     fprintf(afm_file, "EndKernData\n");
  5823. }
  5824.  
  5825.  
  5826. #if 0
  5827.  
  5828. /*
  5829. ** This function is commented out because the information
  5830. ** collected by it is not used anywhere else yet. Now
  5831. ** it only collects the directions of contours. And the
  5832. ** direction of contours gets fixed already in draw_glyf().
  5833. **
  5834. ***********************************************
  5835. **
  5836. ** Here we expect that the paths are already closed.
  5837. ** We also expect that the contours do not intersect
  5838. ** and that curves doesn't cross any border of quadrant.
  5839. **
  5840. ** Find which contours go inside which and what is
  5841. ** their proper direction. Then fix the direction
  5842. ** to make it right.
  5843. **
  5844. */
  5845.  
  5846. #define MAXCONT    1000
  5847.  
  5848. void
  5849. fixcontours(
  5850.         GLYPH * g
  5851. )
  5852. {
  5853.     CONTOUR         cont[MAXCONT];
  5854.     short           ymax[MAXCONT];    /* the highest point */
  5855.     short           xofmax[MAXCONT];    /* X-coordinate of any point
  5856.                          * at ymax */
  5857.     short           ymin[MAXCONT];    /* the lowest point */
  5858.     short           xofmin[MAXCONT];    /* X-coordinate of any point
  5859.                          * at ymin */
  5860.     short           count[MAXCONT];    /* count of lines */
  5861.     char            dir[MAXCONT];    /* in which direction they must go */
  5862.     GENTRY         *start[MAXCONT], *minptr[MAXCONT], *maxptr[MAXCONT];
  5863.     int             ncont;
  5864.     int             i;
  5865.     int             dx1, dy1, dx2, dy2;
  5866.     GENTRY         *ge, *nge;
  5867.  
  5868.     /* find the contours and their most upper/lower points */
  5869.     ncont = 0;
  5870.     ymax[0] = -5000;
  5871.     ymin[0] = 5000;
  5872.     for (ge = g->entries; ge != 0; ge = ge->next) {
  5873.         if (ge->type == GE_LINE || ge->type == GE_CURVE) {
  5874.             if (ge->iy3 > ymax[ncont]) {
  5875.                 ymax[ncont] = ge->iy3;
  5876.                 xofmax[ncont] = ge->ix3;
  5877.                 maxptr[ncont] = ge;
  5878.             }
  5879.             if (ge->iy3 < ymin[ncont]) {
  5880.                 ymin[ncont] = ge->iy3;
  5881.                 xofmin[ncont] = ge->ix3;
  5882.                 minptr[ncont] = ge;
  5883.             }
  5884.         }
  5885.         if (ge->frwd != ge->next) {
  5886.             start[ncont++] = ge->frwd;
  5887.             ymax[ncont] = -5000;
  5888.             ymin[ncont] = 5000;
  5889.         }
  5890.     }
  5891.  
  5892.     /* determine the directions of contours */
  5893.     for (i = 0; i < ncont; i++) {
  5894.         ge = minptr[i];
  5895.         nge = ge->frwd;
  5896.  
  5897.         if (ge->type == GE_CURVE) {
  5898.             dx1 = ge->ix3 - ge->ix2;
  5899.             dy1 = ge->iy3 - ge->iy2;
  5900.  
  5901.             if (dx1 == 0 && dy1 == 0) {    /* a pathological case */
  5902.                 dx1 = ge->ix3 - ge->ix1;
  5903.                 dy1 = ge->iy3 - ge->iy1;
  5904.             }
  5905.             if (dx1 == 0 && dy1 == 0) {    /* a more pathological
  5906.                              * case */
  5907.                 dx1 = ge->ix3 - ge->prev->ix3;
  5908.                 dy1 = ge->iy3 - ge->prev->iy3;
  5909.             }
  5910.         } else {
  5911.             dx1 = ge->ix3 - ge->prev->ix3;
  5912.             dy1 = ge->iy3 - ge->prev->iy3;
  5913.         }
  5914.         if (nge->type == GE_CURVE) {
  5915.             dx2 = ge->ix3 - nge->ix1;
  5916.             dy2 = ge->iy3 - nge->iy1;
  5917.             if (dx1 == 0 && dy1 == 0) {    /* a pathological case */
  5918.                 dx2 = ge->ix3 - nge->ix2;
  5919.                 dy2 = ge->iy3 - nge->iy2;
  5920.             }
  5921.             if (dx1 == 0 && dy1 == 0) {    /* a more pathological
  5922.                              * case */
  5923.                 dx2 = ge->ix3 - nge->ix3;
  5924.                 dy2 = ge->iy3 - nge->iy3;
  5925.             }
  5926.         } else {
  5927.             dx2 = ge->ix3 - nge->ix3;
  5928.             dy2 = ge->iy3 - nge->iy3;
  5929.         }
  5930.  
  5931.         /* compare angles */
  5932.         cont[i].direction = DIR_INNER;
  5933.         if (dy1 == 0) {
  5934.             if (dx1 < 0)
  5935.                 cont[i].direction = DIR_OUTER;
  5936.         } else if (dy2 == 0) {
  5937.             if (dx2 > 0)
  5938.                 cont[i].direction = DIR_OUTER;
  5939.         } else if (dx2 * dy1 < dx1 * dy2)
  5940.             cont[i].direction = DIR_OUTER;
  5941.  
  5942.         cont[i].ymin = ymin[i];
  5943.         cont[i].xofmin = xofmin[i];
  5944.     }
  5945.  
  5946.     /* save the information that may be needed further */
  5947.     g->ncontours = ncont;
  5948.     if (ncont > 0) {
  5949.         g->contours = malloc(sizeof(CONTOUR) * ncont);
  5950.         if (g->contours == 0) {
  5951.             fprintf(stderr, "***** Memory allocation error *****\n");
  5952.             exit(255);
  5953.         }
  5954.         memcpy(g->contours, cont, sizeof(CONTOUR) * ncont);
  5955.     }
  5956. }
  5957.  
  5958. #endif
  5959.  
  5960. /*
  5961.  *
  5962.  */
  5963.  
  5964.